home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / Libraries / VideoToolbox 97.08.16 / VideoToolboxSources / GDVideo.c < prev    next >
Encoding:
Text File  |  1997-06-03  |  79.5 KB  |  2,104 lines  |  [TEXT/CWIE]

  1. /* 
  2. GDVideo.c
  3. Copyright © 1989-1997 Denis G. Pelli
  4.  
  5. Complete set of routines to control video drivers directly, bypassing
  6. QuickDraw's Color and Palette Managers. There is a separate function call for
  7. each of the Control and Status Calls (csc) implemented by video drivers. They
  8. are described in Designing Cards and Drivers, 3rd Ed., chapter 9. A few new
  9. calls are documented in the Display Device Driver Guide Developer Note, which
  10. appeared on the January '94 Developer CD.
  11.  
  12. This file also contains several other helpful routines that deal with video
  13. device drivers. For background, read “Video synch”.
  14.  
  15. GDSetEntries
  16.  
  17. The most useful routine in this file is GDSetEntries. It implements a cscSetEntries Control 
  18. call to the video driver. This directly sets the video device's color lookup table (clut).
  19. (cscSetEntries is documented in Apple's books called
  20. Designing Cards and Drivers and Designing PCI Cards and Drivers.)
  21.  
  22. Apple also provides a high-level SetEntries() call, which is superficially similar, 
  23. but it gets interpreted by QuickDraw in complicated ways before ultimately 
  24. causing a cscSetEntries Control call to the video driver. 
  25. (SetEntries is documented in Inside Macintosh.) 
  26.  
  27. I always prefer GDSetEntries, because I want direct control over the clut.
  28.  
  29. THE MORE-USEFUL (HIGH-LEVEL) ROUTINES: (in alphabetical order)
  30.  
  31. char *GDCardName(GDHandle device);
  32. Returns the address of a C string containing the name of the video card. You
  33. should call DisposePtr() on the returned string when you no longer need it.
  34. Takes about 1.5 ms because Apple's slot routines are very slow. 
  35.  
  36. short GDClutSize(GDHandle device);
  37. Returns the number of entries in the video driver's clut in the current video
  38. mode (i.e. current pixel size). VideoToolbox.h defines a preprocessor macro
  39. GDCLUTSIZE(device) that expands to equivalent inline code.
  40.  
  41. long GDColors(GDHandle device)
  42. Number of colors, in the current mode.
  43.  
  44. short GDDacSize(GDHandle device)
  45. Figures out how many bits in the video dac.
  46.  
  47. OSErr GDSetDelay(GDHandle device,Boolean dontWaitForVBL,double nanoseconds);
  48. OSErr GDGetDelay(GDHandle device,Boolean *dontWaitForVBLPtr,double *nanosecondsPtr);
  49. These two routines support features, i.e. cscSetTimeDelays and cscGetTimeDelays, 
  50. unique to the built-in video driver of the
  51. PowerMac 7500 and 8500. Both routines will merely return innocuous errors (-17
  52. and -18) if used with any other video driver. Like most video drivers, the
  53. 7500/8500 video driver normally waits for VBL when loading the CLUT, but this can
  54. be overriden by the Boolean parameter dontWaitForVBL, in which case, from then on
  55. (until it's changed again or the machine is rebooted) the driver will load the
  56. CLUT immediately when it's called, without waiting for VBL.
  57. (6/3/97: I just discovered that this bit only affects cscSetEntries; cscGetEntries
  58. is unaffected, and never waits for VBL.) The second feature is
  59. that the video driver normally waits 800 ns after loading each RGB triplet to the
  60. CLUT, "to allow for the CLUT to settle and increment its address", and that time
  61. can be set by the parameter "nanoseconds". GDGetDelay() allows you to read the
  62. current setting of both parameters. GDSetDelay() allows you to set both
  63. parameters. If the supplied value of "nanoseconds" is infinite or NAN then it's
  64. ignored and only "dontWaitForVBL" is set. The 7500/8500 video driver was written
  65. by Apple Engineer Fernando Urbina, nano@apple.com. GDSetDelay() and GDSetDelay()
  66. were written by Denis Pelli, to allow convenient access to Fernando's custom
  67. control and status driver calls. Note that Fernando Urbina has also supplied a
  68. newer version of this driver (included in the VideoToolbox) that supports several
  69. extra resolutions, of which the most noteworthy is 640x480x120 Hz, which works
  70. well on the Apple 17" Multiscan.
  71.  
  72. Thus far, using visual inspection in TimeVideo, I've been unable to notice any
  73. bad effects of not waiting for VBL and reducing the waiting interval from 800 ns
  74. to zero. I asked Fernando Urbina, nano@apple.com, about it, since he put in the
  75. delays because he "does indeed get artifacts when updating the CLUT if we don't
  76. suppress interrupts while updating it." What were the artifacts? Fernando
  77. replies, "The delays are there because of hardware requirements:  When writing to
  78. the CLUT in a self-incrementing mode the hardware requires 800ns to update the
  79. address counter.  Of course, this is worst case.  ( We write the CLUT starting
  80. address and then the R, G, and B.  At the end of the B, the address counter for
  81. the CLUT will automatically increment to the next address.) I noticed some
  82. artifacts when doing palette animation -- one of the After Dark modules showed
  83. this very well."
  84.  
  85. OSErr GDGetDisplayMode(GDHandle device,unsigned long *displayModeIDPtr
  86.     ,unsigned short *modePtr,unsigned short *pagePtr,Ptr *baseAddrPtr);
  87. Calls cscGetCurMode, documented in Apple's new Display Device Driver Guide
  88. Developer Note on the January '94 Developer CD. The "display mode ID" is a new
  89. parameter, to support multi-resolution displays. Each display mode may have
  90. multiple pixel depths (which, confusingly, have also been called "modes"). This
  91. call will only be useful if the Display Manager is present (requires PowerPC or
  92. System 7.5) and if you're using the Display Manager, which is documented in a
  93. chapter in Inside Macintosh:Advanced Color Imaging, which thus far has appeared
  94. only in draft form on the December '94 Developer CD. The call returns an error
  95. if the video driver does not support the Display Manager. UNTESTED!!
  96.  
  97. OSErr GDGetEntries(GDHandle device,short start,short count,ColorSpec *table);
  98. Calls cscGetEntries. This is much as you'd expect after reading GDSetEntries
  99. below, EXCEPT that cscGetEntries writes into table[start],.... 
  100. whereas cscSetEntries reads from table[0],.... 
  101. Note that unless the gamma table is linear, the values returned may not
  102. be the same as those originally passed by GDSetEntries. So call
  103. GDUncorrectedGamma first. Try the demo TimeVideo.
  104.  
  105. OSErr GDGetGamma(GDHandle device,GammaTbl **myGammaTblHandle);
  106. Calls cscGetGamma. Returns a pointer to the Gamma table in the specified video
  107. device. (I.e. you pass it a pointer to your pointer, a handle, which it uses to
  108. load your pointer.)
  109.  
  110. OSErr GDGetPageCnt(GDHandle device,short mode,short *pagesPtr);
  111. Calls cscGetPageCnt. Called "GetPages" in Designing Cards and Drivers, 3rd Ed.
  112. You tell it which mode you're interested in. It tells you how many pages of
  113. video ram are available in that mode.
  114.  
  115. Boolean GDHasMode(GDHandle device,short mode,short *pixelSizePtr,short *pagesPtr);
  116. Returns 0 if no such video mode, returns 1 if mode is available.
  117. If pixelSizePtr is not NULL, then sets *pixelSizePtr to pixelSize or -1 if unknown.
  118. If pagesPtr is not NULL, then sets *pagesPtr to pages.
  119.  
  120. unsigned char *GDName(GDHandle device);
  121. Returns a pointer to the name of the driver (a pascal string). This is quick.
  122. The string is part of the driver itself, so don't modify it or try to dispose of it.
  123.  
  124. unsigned char *GDNameStr(GDHandle device);
  125. Returns a pointer to the name of the driver, as a C string. 
  126. The string is static, and will be overwritten by the next call to GDNameStr().
  127.  
  128. ColorSpec *GDNewLinearColorTable(GDHandle device);
  129. Creates a default table for use when gdType==directType.
  130.  
  131. OSErr GDPrintGammaTable(FILE *o,GDHandle device);
  132.  
  133. OSErr GDRestoreDeviceClut(GDHandle device);
  134. Nominally equivalent to Apple's RestoreDeviceClut(), which is only available
  135. since System 6.05. However, I find that Apple's routine sometimes does nothing,
  136. whereas this routine always works. Passing a NULL argument causes restoration of
  137. cluts of all video devices.
  138.  
  139. OSErr GDSaveGamma(GDHandle device);
  140. OSErr GDRestoreGamma(GDHandle device);
  141. Use an internal cache to save and restore the device's gamma-correction table. 
  142. GDSaveGamma(NULL) and GDRestoreGamma(NULL) save and restore ALL devices.
  143. After the first request, GDSaveGamma() ignores subsequent requests to save the same 
  144. device's gamma table. GDRestoreGamma() no longer discards the saved copy (the cache), so multiple
  145. calls to GDRestoreGamma() will all be effective. You should call
  146. GDRestoreGamma before GDRestoreDeviceClut, because the video driver merely saves the gamma
  147. table when you do a cscSetGamma; the driver actually uses the gamma table to transform
  148. a new clut only when loading the clut.
  149.  
  150. OSErr GDSetEntries(GDHandle device,short start,short count,ColorSpec *table);
  151. Does a cscSetEntries call to the video card's driver, loading any
  152. number of clut entries with arbitrary rgb triplets. (Note that the driver will
  153. transform your rgb triplets via the gamma table before loading them into the
  154. clut; so call GDUncorrectedGamma first.) "device" specifies which video device's
  155. clut you want to load. "start" is either in the range 0 to clutSize-1,
  156. indicating which clut entry to load first (in "sequence mode"), or is -1
  157. (requesting "index mode"). "count" is the number of entries to be modified,
  158. minus 1. "table" is a ColorSpec array (not a ColorTable) containing the rgb
  159. triplets that you want to load into the clut. In sequence mode "start" must be
  160. in the range 0 to clutSize-1, the i-th element of table corresponds to the
  161. i-th entry in the clut, and the "value" field of each element of table is
  162. ignored. In index mode "start" must be -1, and the "value" field of each element
  163. of table indicates which clut entry to load it into. The arguments start, count,
  164. and (perhaps) table are the same as for the Color Manager call SetEntries(), documented in
  165. Inside Macintosh V-143. Note that cscSetEntries reads from table[0],.... 
  166. whereas cscGetEntries writes to table[start],.... 
  167. (Most drivers wait for blanking before modifying the
  168. clut. For a full discussion, read the VideoToolbox "Video synch" file.) You may
  169. also want to look at the file SetEntriesQuickly.c, which provides the
  170. functionality of GDSetEntries and GDDirectSetEntries, but bypasses the video
  171. driver to access the hardware directly.
  172.  
  173. OSErr GDSetEntriesByType(GDHandle device,short start,short count,ColorSpec *table);
  174. Checks the (**device).gdType field and calls cscSetEntries, cscDirectSetEntries,
  175. or nothing, as appropriate.
  176.  
  177. OSErr GDSetEntriesByTypeHighPriority(GDHandle device,short start,short count
  178.     ,ColorSpec *table);
  179. Calls GDSetEntriesByType() while the processor priority has been temporarily 
  180. raised to 7.
  181.  
  182. OSErr GDSetGamma(GDHandle device, GammaTbl *gamma);
  183. Calls cscSetGamma. Loads a Gamma table into the specified video device. The
  184. video driver will make a copy of your table. You can discard your table after
  185. making this call. Note that the video driver only uses the gamma table when
  186. performing cscSetEntries, i.e. when actually loading the clut. The structure of the
  187. gamma table is (finally!) documented in Designing Cards and Drivers, 3rd
  188. edition, pages 215-216. Beware of a discrepancy between the documentation and
  189. the definition in QuickDraw.h: gFormulaData is described as a byte array in the
  190. text, but is declared as a short array in the QuickDraw.h header file. NOTE: a
  191. few video drivers (Radius PowerView and SuperMac ColorCard) do not support gamma
  192. tables. The SuperMac ColorCard is well behaved, giving the appropriate error
  193. code, returned by GDSetGamma and GDGetGamma. The Radius PowerView reports no
  194. error yet ignores the GDSetGamma call and returns a table full of zeroes in
  195. response to GDGetGamma. See NOTE from Tom Busey below. However, if all you want
  196. to do is call GDUncorrectedGamma, then these limitations won't affect you,
  197. because the drivers that lack gamma tables always give you precisely the
  198. behavior that one requests by calling GDUncorrectedGamma.
  199.  
  200. OSErr GDSetPageDrawn(GDHandle device,short page);
  201. Choose which page of video memory will be used by future drawing operations. 
  202. Untested.
  203.  
  204. OSErr GDSetPageShown(GDHandle device,short page);
  205. Choose which page of video memory we see. Untested.
  206.  
  207. OSErr GDUncorrectedGamma(GDHandle device);
  208. Asks GDSetGamma to load a linear gamma table, i.e. no correction. (The gamma
  209. correction implemented by table-lookup in the video driver is too crude for
  210. experiments that want accurate luminance control.)
  211.  
  212. int GDVersion(GDHandle device)
  213. Returns the version number of the driver. From the first word-aligned word after
  214. the name string. This is quick.
  215.  
  216. OSErr GDGetNextResolution()
  217. Implements cscGetNextResolution.
  218.  
  219. LESS-USEFUL (LOW LEVEL) ROUTINES:
  220.  
  221. OSErr GDControl(int refNum,int csCode,Ptr csParamPtr)
  222. Uses low-level PBControl() call to implement a "Control()" call that works! 
  223. I don't know why this wasn't discussed in Apple Tech Note 262.
  224.  
  225. OSErr GDDirectSetEntries(GDHandle device,short start,short count,ColorSpec *table);
  226. Calls cscDirectSetEntries. If your pixel depth is >8 then the cscSetEntries call is
  227. disabled, and you must use this instead of GDSetEntries().
  228.  
  229. VideoDriver *GDDriverAddress(GDHandle device);
  230. Returns a pointer to the driver, whether in ROM or RAM. Neither this prototype
  231. nor the definition of the VideoDriver structure are included in VideoToolbox.h.
  232.  
  233. OSErr GDGetDefaultMode(GDHandle device,short *modePtr)
  234. Calls cscGetDefaultMode. It tells you what the default mode is. I'm not sure
  235. what this means.
  236.  
  237. OSErr GDGetGray(GDHandle device,Boolean *flagPtr);
  238. Calls cscGetGray. Get gray flag. 0 for color. 1 if all colors mapped to
  239. luminance-equivalent gray tones.
  240.  
  241. OSErr GDGetInterrupt(GDHandle device,Boolean *flagPtr);
  242. Calls cscGetInterrupt. Get flag. 1 if VBL interrupts of this card are enabled. 0
  243. if disabled.
  244.  
  245. OSErr GDGetMode(GDHandle device,short *modePtr,short *pagePtr,Ptr *baseAddrPtr);
  246. Calls cscGetMode. It tells you the current mode, page of video memory, and the
  247. base address of that page.
  248.  
  249. OSErr GDGetPageBase(GDHandle device,short page,Ptr *baseAddrPtr);
  250. Calls cscGetPageBase. (Called "cscGetBaseAddr" in Designing Cards and Drivers, 3rd
  251. Ed.) You tell it which page of video memory you're interested in (in the current
  252. video mode). It tells you the base address of that page.
  253.  
  254. OSErr GDGrayPage(GDHandle device,short page);
  255. Calls cscGrayPage. (Called "cscGrayScreen" in Designing Cards and Drivers, 3rd
  256. Ed.) Fills the specified page with gray, i.e. the dithered desktop pattern. I'm
  257. not aware of any particular advantage in using this instead of FillRect().
  258. Designing Cards and Drivers, 3rd Edition, Chapter 9, says that for direct
  259. devices, i.e. >8 bit pixels, the driver will also load a linear,
  260. gamma-corrected, gray color table.
  261.  
  262. OSErr GDReset(GDHandle device, short *modePtr, short *pagePtr, Ptr *baseAddrPtr);
  263. Calls cscReset. Initialize the video card to its startup state, usually 1 bit
  264. per pixel. Returns the parameters of that state.
  265.  
  266. OSErr GDSetDefaultMode(GDHandle device,short mode);
  267. Calls cscSetDefault. Supposedly, you tell it what mode you want to start up with
  268. when you reboot. (I've never been able to get this to work. No error and no
  269. effect. Perhaps I've misunderstood its purpose.)
  270.  
  271. OSErr GDSetGray(GDHandle device,Boolean flag);
  272. Calls cscSetGray. Set gray flag. 0 for color. 1 if all colors mapped to
  273. luminance-equivalent gray tones.
  274.  
  275. OSErr GDSetInterrupt(GDHandle device,Boolean flag);
  276. Calls cscSetInterrupt. Set flag to 1 to enable VBL interrupts of this card, or 0
  277. to disable.
  278.  
  279. OSErr GDSetMode(GDHandle device,short mode,short page,Ptr *baseAddrPtr);
  280. Calls cscSetMode. You tell it what mode and video page you want. It sets 'em and
  281. returns the base address of that page in video memory. Also, because Apple said
  282. so, the video driver sets the entire clut to 50% gray. Note that this changes
  283. things behind QuickDraw's back. For most applications life will be simpler if
  284. you overtly ask QuickDraw to take charge by calling Apple's SetDepth() instead
  285. of GDSetMode(). WARNING: Apple now considers the mode numbers merely ordinal.
  286. E.g. on the 8•24 video card the 32-bit mode has mode number 0x84, not 0x85 as
  287. you might have expected on the basis of old Apple documentation and other Apple
  288. video cards.
  289.  
  290. OSErr GDSwitchMode(GDHandle device,short mode,long displayModeID,short page,Ptr *baseAddrPtr);
  291. Calls cscSwitchMode as documented in Designing PCI Video Cards and Drivers. PCI
  292. video drivers are required to support this call. It's like GDSetMode, but additionally
  293. allows you to specify the displayModeID, which determines the spatial resolution.
  294.  
  295. OSErr GDStatus(int refNum,int csCode,Ptr csParamPtr)
  296. Uses low-level PBStatus() call to implement a "Status()" call that works! The
  297. need for this is explained in Apple Tech Note 262, which was issued in response
  298. to my bug report in summer of '89.
  299.  
  300. PatchMacIIciVideoDriver();
  301. Fixes a crashing bug in the Mac IIci built-in video driver's implementation of
  302. cscGetEntries. The patch persists until reboot. It is unlikely that you will need
  303. to call this explicitly, PatchMacIIciVideoDriver() is automatically invoked the
  304. first time GDGetEntries is called. The Mac IIci built-in video driver
  305. (.Display_Video_Apple_RBV1 driver, version 0) has a bug that causes it to crash
  306. if you try to do a cscGetEntries Status request. PatchMacIIciVideoDriver() finds and
  307. patches the copy of the buggy driver residing in memory. (If the driver is
  308. ROM-based it first moves it to RAM, and then patches that.) Only two instructions
  309. are modified, to save & restore more registers. This fix persists only until the
  310. next reboot. If the patch is successfully applied the version number is increased
  311. by 100.
  312.  
  313. NOTES:
  314.  
  315. Several bugs in version 2 (in System ≤6.03) of the video driver for Apple's
  316. "Toby Frame Buffer" video card that affected use of the GetGamma call were
  317. fixed in version 3 (in System 6.04), apparently in response to my bug report to
  318. Apple.
  319.  
  320. The control/status-call parameter in a GDControl or GDStatus call specifies what
  321. you want done. See Designing Cards and Drivers, 3rd Edition, Chapter 9. (A few
  322. new calls are documented in the Display Device Driver Guide Developer Note.)
  323.  Note that sometime around 1990 Apple renamed some of the Control and Status
  324. calls, giving them names that better reflect their function. I followed suit.
  325. However, Apple neglected to update their book, Designing Cards and Drivers, now
  326. in its 3rd edition. Fortunately, they define both names in their Video.h header
  327. file. To avoid confusion, here are the equivalences. Note that "csc" stands for
  328. "Control and Status Call"
  329. Control call:
  330.  cscGrayPage =  cscGrayScreen = 5
  331. Status calls:
  332.  cscGetPageCnt = cscGetPages = 4
  333.  cscGetPageBase = cscGetBaseAddr = 5
  334.  
  335. Based on:
  336. Inside Macintosh-II (Device Manager)
  337. Designing Cards and Drivers, 3rd Edition, Chapter 9.
  338. Display Device Driver Guide Developer Note on the January '94 Developer CD.
  339. Tech Note 262: "Controlling Status Calls"
  340. "GreyScale Information" from AppleLink "Apple Technical Info"
  341. "Video Configuration ROM Software Specification" also from AppleLink,
  342. in "Developer Tech Support:Macintosh:32 Bit QuickDraw"
  343.  
  344. NOTES:
  345. Tom Busey (busey@indiana.edu) writes: "I'm using an 8-bit SuperMac ColorCard
  346. that a used computer dealer gave me when I ordered a Toby card. I discovered
  347. that the status and control calls for GDUncorrectedGamma return -17 and -18
  348. respectively, which means that the driver doesn't support different gammas.
  349. GDUncorrectedGamma returns an error message, but GDOpenWindow doesn't use the
  350. error message or pass it back to the calling program. I'm wondering if there are
  351. people using GDOpenWindow who think that they are getting a linear gamma but who
  352. are actually getting an error message and not learning about it."
  353.      Tom's facts are right, but he needn't worry. A few video drivers, e.g.
  354. Radius PowerView, and apparently the SuperMac ColorCard, don't implement gamma
  355. tables at all, but the error from GDUncorrectedGamma is probably not a concern.
  356. The real test is to run TimeVideo. TimeVideo does a write-and-read-back test of
  357. the clut of your video card. If that test passes then you can safely conclude
  358. that there's no gamma translation going on. So, as nearly every document in the
  359. VideoToolbox says: run TimeVideo and read the report.
  360.      Here's how gamma tables work. According to the Apple manual (Designing
  361. Cards and Drivers), the values in the rgb triplets that you supply in a
  362. cscSetEntries call to the video driver are first translated via the gamma table
  363. (each r,g, and b component separately) before being loaded into the CLUT
  364. hardware table. Most drivers maintain a gamma table internally (in computer
  365. memory) and allow you to get and set it via the cscGetGamma and cscSetGamma
  366. calls. A few video drivers (Radius PowerView, SuperMac ColorCard) don't support
  367. gamma tables. They load your rgb values directly into the CLUT, without
  368. translation. However, for users of the VideoToolbox that's usually fine, since
  369. that's exactly the behavior that we routinely request by calling
  370. GDUncorrectedGamma. Of course, if you want to load a custom gamma table, other
  371. than the identity transformation, then you'll be calling GDSetGamma, and you
  372. should make sure it does not return an error. (Alas, the Radius PowerView driver
  373. more or less ignores the cscSetGamma and cscGetGamma requests--GDGetGamma
  374. returns a table that's all zeroes--but the driver fails to report an error. I
  375. wrote to the Radius programmer a year ago, but they're no longer interested in
  376. working on that product.)
  377.     Patrick Flanagan (flanagan@deakin.edu.au) writes: "Can I be 100% sure, if I
  378. select "Special Gamma", in the Monitors Control Panel and choose "uncorrected
  379. gamma" for a video card, that this is the same as using GDUncorrectedGamma?"
  380. REPLY: That is what I would expect from reading all the relevant documentation.
  381. However, to be 100% certain of anything I would want to check it myself.
  382. TimeVideo calls GDUncorrectedGamma and then confirms that write-then-read tests
  383. of the CLUT return what was written. (Gamma-table translation is only done on
  384. the write, not undone on the read.) Thus TimeVideo will confirm for you that
  385. after calling GDUncorrectedGamma your video card has what Apple calls an
  386. "uncorrected" gamma table. However, TimeVideo does not check what you would get
  387. by merely selecting "uncorrected" gamma in the Monitors Control Panel. It would
  388. be reasonable to assume that you would get the same result since Monitors
  389. interacts with the video driver by the same Control and Status Calls as
  390. GDVideo.c does, so it must use the same cscSetGamma call as is used by
  391. GDUncorrectedGamma.
  392.  
  393. HISTORY:
  394. 9/29/89     dgp    added caution above that successive calls to cscSetEntries may be on one 
  395.                 frame.
  396. 11/21/89    dgp    corrected mode list: 0x80... as pointed out by Chuck Stein
  397. 11/30/89    dgp    added note above from Don Kelly.
  398.                 Added cautionary note above about GDSetEntries().
  399. 3/1/90        dgp    updated comments.
  400. 3/3/90        dgp    included Video.h instead of defining VDSetEntryRecord and VDPageInfo.
  401. 3/20/90        dgp    made compatible with MPW C.
  402. 3/23/90        dgp    changed char to unsigned char in VDDefModeRec
  403.                 and VDFlagRec to prevent undesirable sign extension.
  404. 4/2/90        dgp    Deleted mode argument from GDGrayScreen().
  405. 7/28/90        dgp Fixed stack overflow in GDGrayScreen, by declaring SysEnvRec static.
  406. 10/20/90    dgp    Apple has renamed the control and status calls, so I followed suit:
  407.                 •GDGetPageBase replaces GDGetBaseAddr
  408.                 •GDReset replaces GDInit
  409.                 •GDGrayPage replace GDGrayScreen
  410.                 •GDGetPageCnt replaces GDGetPages
  411. 2/12/91        dgp Discovered that the old bug in GDGrayPage is still present in System
  412.                 6.05, so I removed the conditional around the bug fix. TestGDVideo
  413.                 now works with: Mac II Video Card, Mac II Display Card (4•8), 
  414.                 Mac IIci Built-in Video, TrueVision NuVista, Mac IIsi Built-in Video.
  415. 7/22/91        dgp    Changed definition of csGTable from GammaTblPtr to Ptr, 
  416.                 to conform with MPW C.
  417. 8/24/91        dgp    Made compatible with THINK C 5.0.
  418. 10/22/91    dgp    With help from Bart Farell, converted all functions headers to
  419.                 Standard C style.
  420. 8/26/92        dgp    added a few miscellaneous comments
  421.                 In all routines, *baseAddrPtr is now set only if baseAddrPtr is not NULL.
  422. 10/10/92    dgp    Added GDRestoreDeviceClut(). Removed obsolete support for THINK C 4.
  423. 11/30/92    dgp corrected error in comment documenting values of argument to GDSetGray().
  424.                 Set flag to zero for color,1 for luminance-mapped gray.
  425. 12/9/92        dgp    Enhanced GDRestoreDeviceClut(). Passing a NULL argument now requests
  426.                 application to all devices. I only just learned that Apple's
  427.                 RestoreDeviceClut() behaves in this way.
  428. 12/15/92    dgp Renamed GDRestoreDeviceClut to GDRestoreDeviceClut to be consistent
  429.                 with Apple's capitalization of RestoreDeviceClut.
  430. 12/16/92    dgp Updated comments to be consistent with 3rd edition of Designing Cards
  431.                 and Drivers. •Renamed myGDHandle to "device", which is easier to read.
  432.                 Renamed GDLinearGamma to GDUncorrectedGamma, and generalized it
  433.                 to work with any size DAC. For compatibility with old programs
  434.                 VideoToolbox.h now has a #define statement making GDLinearGamma
  435.                 an alias for GDUncorrectedGamma.
  436. 12/30/92    dgp Updated some of the comments. Eliminated Files.h.
  437. 12/30/92    dgp Use GDClutSize() to determine clut size.
  438. 1/4/93        dgp In GDClutSize, check for fixedType.
  439. 1/5/93        dgp In GDClutSize, only set last clut entry.
  440.                 Added PatchMacIIciVideoDriver() to the end of this file, and 
  441.                 automatically invoke it the first time GDGetEntries is called.
  442. 1/6/93        dgp    Cleaned up GDClutSize. It now seems to work correctly in the direct modes.
  443. 1/18/93    dgp    Spruced up the documentation.
  444.             •Added all the code formerly in GDIdentify.c, but omitted the obsolete 
  445.             function GDIdentify(), which simply printed GDName() and GDVersion.
  446.             •Enhanced GDGetEntries() to avoid calling drivers that are known
  447.             to crash, returning a statusErr instead.
  448.             •Recoded PatchMacIIciVideoDriver() so as not to call GetScreenDevice.c.
  449. 2/1/93    dgp    Fixed endless loop in PatchMacIIciVideoDriver. Enhanced to deal with
  450.             ROM-based drivers as well.
  451. 2/5/93    dgp    Expanded the documentation of GDSetEntries above, and supplied sample
  452.             code showing how to load the clut.
  453. 2/7/93    dgp Fixed endless loop in PatchMacIIciVideoDriver.
  454. 2/20/93    dgp    Fixed bug in GDUncorrectedGamma() that was causing TestCluts to fail
  455.             for video devices that have nonstandard gamma tables. 
  456. 3/18/93    dgp    Fixed divide by zero error in GDClutSize.
  457. 4/6/93    dgp    GDGetPageCnt() now sets *pagePtr argument only if no error occurs.
  458.             Deleted GDModeName(). Removed assumption, in all routines, that there
  459.             is any particular correspondence between the video mode number and
  460.             the pixel size. Added two new routines, GDPixelSize and GDType,
  461.             that return the pixelSize and gdType (i.e. fixedType, clutType, or 
  462.             directType) of the device. Completely rewrote GDClutSize.
  463. 4/16/93    dgp    Streamlined GDClutSize() to make it fast enough for routine use.
  464. 4/19/93    dgp    Added GDNewLinearColorTable.
  465. 4/25/93    dgp    Made CntrlParam automatic instead of static.
  466. 4/25/93    dgp    Added GDColors(device) and GDPrintGammaTable(). Alphabetized the lists
  467.             of functions in the documentation above.
  468. 5/11/93    dgp    Changed GDPrintGammaTable() to accept a simple file pointer instead of
  469.             an array of two file pointers.
  470. 7/7/93    dgp enhanced GDDacSize() to return 8 unless the gamma table is present and
  471.             reasonable.
  472. 7/29/94    dgp    added GDNameStr().
  473. 9/5/94 dgp removed assumption in printf's that int==short.
  474. 10/25/94 dgp check for missing driver, (*device)->gdRefNum==0, and treat that case
  475.             just like  device==NULL. Devices created by NewGWorld have no driver.
  476. 12/29/94 dgp added GDGetDisplayMode(), calls cscGetCurMode, based on Apple's new
  477.             Display Device Driver Guide Developer Note on the January '94 Developer CD.
  478. 1/15/94 dgp GDPixelSize if supplied with a merely device record, with no associated 
  479.             device driver, then we just return (**(**device).gdPMap).pixelSize
  480. 3/22/95 dgp Added .Display_Video_Apple_DOMEMax to the list of drivers that don't
  481.             support cscGetEntries. Crash reported by Greg Jackson.
  482. 4/6/95 dgp Changed GDUncorrectedGamma() to pass a NULL gamma table pointer instead of
  483.             a linear gamma table, in the hopes that this might work even with the
  484.             few video drivers that don't fully support cscSetGamma.
  485. 4/21/95 dgp Fixed conditionals to cope with various versions of Video.h
  486. 6/8/95 dgp Made sure that the Mac structs are always 68k aligned.
  487. 6/26/95 dgp fixed error in dealing with UNIVERSAL_HEADERS==1 reported by Bart Farell.
  488.         We need the typedefs for DisplayModeID and DepthMode for universal headers
  489.         absent and also for version 1.
  490. 6/27/95 dgp Same for enum cscGetNextResolution.
  491. 6/30/95 dgp GDSaveGamma(NULL) and GDRestoreGamma(NULL) now save and restore ALL devices.
  492. 7/3/95 dgp After the first request, GDSaveGamma() ignores subsequent requests to save the same 
  493.         device's gamma table. GDRestoreGamma() no longer discards the saved copy, so multiple
  494.         calls to GDRestoreGamma() will all be effective. The effect of these changes is that
  495.         if you have multiple nested pairs of calls to GDSaveGamma and GDRestoreGamma, you will
  496.         end up with the desired result: every RestoreGamma will restore the screen to its
  497.         original gamma, same as it had at the time of the first call to GDSaveGamma.
  498. 8/10/95 bt & dgp. Bosco Tjan, tjan@cs.umn.edu, found that the conditionals needed to be 
  499.         adjusted to be compatible with Symantec C 8, because their Video.h is different from Apple's.
  500. 9/26/95 dgp. For compatibility with the ETO18 (CW7) universal headers, eliminated the now obsolete
  501.         typedefs of DepthMode (unsigned short) and DisplayModeID (unsigned long).
  502. 10/4/95 dgp Added code to GDCardName() to check for the presence of the Slot Manager, and
  503.         just return an empty string "" if it's not present. This will prevent crashes on PCI Macs.
  504. 10/4/95 dgp Added GDSwitchMode() which implements cscSwitchMode as documented in Designing PCI 
  505.         Video Cards and Drivers.
  506. 10/24/95 dgp Added GDGetDelay and GDSetDelay to implement the custom video driver calls
  507.         offered by the 7500/8500 video driver.
  508. 10/25/95 dgp After clarification from Fernando, fixed bug in GDGetDelays, so it should
  509.         now work reliably. I didn't know that I had to set the validMask bits.
  510. 12/4/95 dgp As requested by David Brainard, added #def of cscSwitchMode for backward compatibility
  511.         with older versions of THINK C.
  512. 3/4/96 dgp made compatible with THINK C 8, by replacing "SYMANTEC_C" by "SYMANTEC_C || THINK_C".
  513. 3/7/96 dgp Note that cscSetEntries reads from table[0],.... whereas cscGetEntries writes to table[start],.... 
  514. 5/1/96 dgp added GDDisposeGamma() to free the gamma tables.
  515. 5/28/96 dgp added test for UNIVERSAL_INTERFACES_VERSION<0x0212.
  516. 1/15/97    dgp    added explanatory comment to GDSaveGamma().
  517. 4/2/97    dgp    Cosmetic.
  518. 4/10/97    dgp    Eliminate stuff that was conditional on !UNIVERSAL_HEADERS.
  519. 6/3/97    dgp    Deleted obsolete debugging code from GDSet/GetDelays.
  520. */
  521.  
  522. #include "VideoToolbox.h"
  523. #include <ROMDefs.h>    // catDisplay,typeVideo,sRsrcName,sGammaDir
  524. //#include <Displays.h>
  525. void GetCPUProperties(char *cpuName,long *cpuHz,Boolean *hasL2Cache);
  526. void GetVideoProperties(GDHandle device,char *slotName,char *cardName,char *modelName);
  527. Boolean IsPCIMac(void);
  528. OSErr GDSwitchMode(GDHandle device,short mode,long displayModeID,short page,Ptr *baseAddrPtr);
  529.  
  530. #if PRAGMA_ALIGN_SUPPORTED || __MWERKS__
  531.     #pragma options align=mac68k
  532. #endif
  533.  
  534. #if UNIVERSAL_HEADERS<2
  535.     enum{dRAMBasedMask=0x0040};    /* dCtlDriver is a handle (1) or pointer (0) */
  536. #endif
  537. #if (UNIVERSAL_HEADERS<2) || SYMANTEC_C || THINK_C || (__MWERKS__>=0x700)
  538.     /* These declarations appeared in Apple's Video.h, universal version 2.0a3, */
  539.     /* and in "Universal Interfaces  DDK v23c5.  Monday, May 15, 1995"
  540.     /* but not in previous or subsequent versions (e.g. 2.1 aka ETO18),
  541.     /* until version 2.1.2 ETO 20, May 1996. */
  542.     // The version macro UNIVERSAL_INTERFACES_VERSION is defined in Apple's ConditionalMacros.h,
  543.     // but only since version 2.1 (which was distributed with CodeWarrior 8).
  544.     #if UNIVERSAL_INTERFACES_VERSION<0x0212
  545.         struct VDResolutionInfoRec {
  546.             unsigned long                    csPreviousDisplayModeID;    /* ID of the previous resolution in a chain */
  547.             unsigned long                    csDisplayModeID;            /* ID of the next resolution */
  548.             unsigned long                    csHorizontalPixels;            /* # of pixels in a horizontal line */
  549.             unsigned long                    csVerticalLines;            /* # of lines in a screen */
  550.             Fixed                            csRefreshRate;                /* Vertical Refresh Rate in Hz */
  551.             unsigned short                    csMaxDepthMode;                /* 0x80-based number representing max bit depth */
  552.             unsigned long                    csResolutionFlags;            /* flag bits */
  553.             unsigned long                    csReserved;                    /* Reserved */
  554.         };
  555.         typedef struct VDResolutionInfoRec VDResolutionInfoRec;
  556.         typedef VDResolutionInfoRec *VDResolutionInfoPtr;
  557.         #define cscGetNextResolution 17    /* this won't break even if cscSwitchMode has already been declared as an enum */
  558.         #define cscSwitchMode 10        /* this won't break even if cscSwitchMode has already been declared as an enum */
  559.     #endif
  560. #endif
  561.  
  562. typedef struct VDFlagRec {
  563.     unsigned char flag;
  564.     char pad;
  565. } VDFlagRec;
  566.  
  567. typedef struct VDDefModeRec{
  568.     unsigned char spID;
  569.     char pad;
  570. } VDDefModeRec;
  571.  
  572. typedef struct {
  573.     short flags;
  574.     short blanks[3];
  575.     short open;
  576.     short prime;
  577.     short control;
  578.     short status;
  579.     short close;
  580.     Str255 name;
  581. } VideoDriver;
  582. VideoDriver *GDDriverAddress(GDHandle device);
  583.  
  584. typedef struct {
  585.     short csCode;        // control code
  586.     short length;        // total parameter block bytes
  587.     char param[];        // control call data
  588. } ScrnCtl;
  589.  
  590. typedef struct {
  591.     short spDrvrHw;        // Slot Manager ID
  592.     short slot;            // Number of slot
  593.     long dCtlDevBase;    // Start of device's address space
  594.     short mode;            // screen characteristics
  595.     short flagMask;        // Which flag bits are used
  596.     short flags;        // device state: bit 0 = 0 = mono; bit 0 = 1 = color;
  597.                         // bit 11 =  1 = startup device; bit 15 = 1 = active
  598.     short colorTable;    // 'clut' id, default = -1
  599.     short gammaTable;    // Selects color intensity, default (MacII) = -1
  600.     Rect globalRect;    // global rectangle, main device topLeft = 0,0
  601.     short ctlCount;        // total control calls
  602.     ScrnCtl ctl;
  603. } Scrn;
  604.  
  605. typedef struct {
  606.     short scrnCount;    // Total devices
  607.     Scrn scrn;
  608. } Scrns;                // 'scrn' resource
  609.  
  610. #if PRAGMA_ALIGN_SUPPORTED || __MWERKS__
  611.     #pragma options align=reset
  612. #endif
  613.  
  614. Scrn **GDGetScrn(GDHandle device);
  615.  
  616. OSErr GDGetNextResolution(GDHandle device,unsigned long previousDisplayModeID
  617.     ,unsigned long *displayModeIDPtr,unsigned long *horizontalPixelsPtr
  618.     ,unsigned long *verticalLinesPtr,Fixed *refreshRatePtr
  619.     ,unsigned short *maxDepthModePtr);
  620.  
  621. OSErr GDGetDisplayMode(GDHandle device,unsigned long *displayModeIDPtr
  622.     ,unsigned short *modePtr,unsigned short *pagePtr,Ptr *baseAddrPtr)
  623. /*
  624. It tells you the current display mode, etc., but only if the video driver 
  625. supports the Display Manager.
  626. */
  627. {
  628.     VDSwitchInfoRec vdSwitchInfo;
  629.     int error;
  630.  
  631.     if(device==NULL || (*device)->gdRefNum==0) return statusErr;
  632.     error=GDStatus((*device)->gdRefNum,cscGetCurMode,(Ptr) &vdSwitchInfo);
  633.     if(displayModeIDPtr!=NULL) *displayModeIDPtr=vdSwitchInfo.csData;
  634.     if(modePtr!=NULL) *modePtr=vdSwitchInfo.csMode;
  635.     if(pagePtr!=NULL) *pagePtr=vdSwitchInfo.csPage;
  636.     if(baseAddrPtr!=NULL) *baseAddrPtr=vdSwitchInfo.csBaseAddr;
  637.     return error;
  638. }
  639.  
  640. OSErr GDGetNextResolution(GDHandle device,unsigned long previousDisplayModeID
  641.     ,unsigned long *displayModeIDPtr,unsigned long *horizontalPixelsPtr
  642.     ,unsigned long *verticalLinesPtr,Fixed *refreshRatePtr
  643.     ,unsigned short *maxDepthModePtr)
  644. /*
  645. Tells you the next available displayModeID, after previousDisplayModeID.
  646. This is a new call, introduced for PCI video drivers.
  647. */
  648. {
  649.     VDResolutionInfoRec    vdSwitchInfo;
  650.     int error;
  651.  
  652.     if(device==NULL || (*device)->gdRefNum==0) return statusErr;
  653.     vdSwitchInfo.csPreviousDisplayModeID=previousDisplayModeID;
  654.     error=GDStatus((*device)->gdRefNum,cscGetNextResolution,(Ptr) &vdSwitchInfo);
  655.     if(displayModeIDPtr!=NULL) *displayModeIDPtr=vdSwitchInfo.csDisplayModeID;
  656.     if(horizontalPixelsPtr!=NULL) *horizontalPixelsPtr=vdSwitchInfo.csHorizontalPixels;
  657.     if(verticalLinesPtr!=NULL) *verticalLinesPtr=vdSwitchInfo.csVerticalLines;
  658.     if(refreshRatePtr!=NULL) *refreshRatePtr=vdSwitchInfo.csRefreshRate;
  659.     if(maxDepthModePtr!=NULL) *maxDepthModePtr=vdSwitchInfo.csMaxDepthMode;
  660.     return error;
  661. }
  662.  
  663. OSErr GDSetPageDrawn(GDHandle device,short page)
  664. // Select a page of video memory to draw into.
  665. {
  666.     int error;
  667.     short flags;
  668.     Ptr baseAddr;
  669.     static long qD=-1;
  670.  
  671.     if(qD==-1)Gestalt(gestaltQuickdrawVersion,&qD);
  672.     error=GDGetPageBase(device,page,&baseAddr);
  673.     if(!error){
  674.         if(qD>=gestalt32BitQD){
  675.             flags=GetPixelsState((**device).gdPMap);
  676.             LockPixels((**device).gdPMap);
  677.         }
  678.         (**(**device).gdPMap).baseAddr=baseAddr;
  679.         if(qD>=gestalt32BitQD){
  680.             GDeviceChanged(device);
  681.             SetPixelsState((**device).gdPMap,flags);
  682.         }
  683.     }
  684.     return error;
  685. }
  686.  
  687. OSErr GDSetPageShown(GDHandle device,short page)
  688. // Select a page of video memory for display.
  689. {
  690.     int error;
  691.     short mode;
  692.  
  693.     error=GDGetMode(device,&mode,NULL,NULL);
  694.     if(error)return error;
  695.     return GDSetMode(device,mode,page,NULL);
  696. }
  697.  
  698. short GDType(GDHandle device)
  699. // Returns what would normally be in (**device).gdType, for occasions when
  700. // the GDevice record might be invalid because you called GDSetMode().
  701. {
  702.     int error;
  703.     static ColorSpec white={255,0xffff,0xffff,0xffff},black={0,0,0,0};
  704.  
  705.     error=GDSetEntries(device,0,0,&white);
  706.     if(!error)return clutType;
  707.     error=GDDirectSetEntries(device,0,0,&black);
  708.     if(!error)return directType;
  709.     return fixedType;
  710. }
  711.  
  712. short GDPixelSize(GDHandle device)
  713. // Returns what would normally be in (**(**device).gdPMap).pixelSize, for occasions
  714. // when the GDevice record might be invalid because you called GDSetMode().
  715. // Exception: if this is merely a device record, with no associated device driver,
  716. // then we just return (**(**device).gdPMap).pixelSize
  717. {
  718.     int error;
  719.     short mode;
  720.  
  721.     if((**device).gdRefNum==0)return (**(**device).gdPMap).pixelSize;
  722.     error=GDGetMode(device,&mode,NULL,NULL);
  723.     return GDModePixelSize(device,mode);
  724. }
  725.  
  726. short GDModePixelSize(GDHandle device,short mode)
  727. // Returns the pixelSize associated with a given video mode.
  728. // If you've changed the mode by calling GDSetMode and you're running a System 
  729. // older than 6.0.5 the answer may may be wrong for some of the newer video cards,
  730. // because they have ideosynchratic associations of video mode and pixel size.
  731. {
  732.     short j;
  733.     long version;
  734.  
  735.     if(mode==(**device).gdMode)return (**(**device).gdPMap).pixelSize;
  736.     Gestalt(gestaltSystemVersion,&version);
  737.     if(version>=0x605){            // Need new Palette Manager for reliable answer.
  738.         for(j=5;j>=0;j--)if(mode==HasDepth(device,1<<j,0,0))return 1<<j;
  739.     } else return 1<<(mode&7);    // Unreliable.
  740.     return 0;
  741. }
  742.  
  743. Boolean GDHasMode(GDHandle device,short mode,short *pixelSizePtr,short *pagesPtr);
  744.  
  745. Boolean GDHasMode(GDHandle device,short mode,short *pixelSizePtr,short *pagesPtr)
  746. // Returns 0 if no such mode, returns 1 if mode is available.
  747. // If pixelSizePtr is not NULL, then sets *pixelSizePtr to pixelSize or -1 if unknown.
  748. // If pagesPtr is not NULL, then sets *pagesPtr to pages.
  749. {
  750.     short pixelSize,i,hasDepthWorks,pages;
  751.     int error;
  752.     long system;
  753.     
  754.     Gestalt(gestaltSystemVersion,&system);
  755.     // On Mac IIci, Sys 6.07, HasDepth returns "mode" of 0x100 at all legal depths.
  756.     hasDepthWorks= system>=0x605     // New Palette Manager.
  757.         && HasDepth(device,(**(**device).gdPMap).pixelSize,0,0)==(**device).gdMode;
  758.     if(hasDepthWorks){
  759.         for(i=0;i<6;i++){
  760.             pixelSize=1<<i;
  761.             if(mode!=HasDepth(device,pixelSize,0,0))continue;
  762.             if(pixelSizePtr!=NULL)*pixelSizePtr=pixelSize;
  763.             if(pagesPtr!=NULL){
  764.                 error=GDGetPageCnt(device,mode,&pages);
  765.                 if(error)pages=1;
  766.                 *pagesPtr=pages;
  767.             }
  768.             return 1;
  769.         }
  770.         return 0;
  771.     }else{
  772.         // If HasDepth doesn't work properly then we can still find out whether the
  773.         // mode is available by asking the video driver for a page count, but
  774.         // I don't know of any discreet way to find out the pixelSize.
  775.         // Calling SetDepth would work, but that would irritate the user who has to
  776.         // watch and wait. 
  777.         error=GDGetPageCnt(device,mode,&pages);
  778.         if(error)pages=0;
  779.         if(pagesPtr!=NULL)*pagesPtr=pages;
  780.         if(mode==(**device).gdMode)pixelSize=(**(**device).gdPMap).pixelSize;
  781.         else pixelSize=-1;        // Unknown.
  782.         if(pixelSizePtr!=NULL)*pixelSizePtr=pixelSize;
  783.         return (pages>0);
  784.     }
  785. }
  786.  
  787. short gdClutSizeTable[33]={0,1<<1,1<<2,0,1<<4,0,0,0,1<<8,0,0,0,0,0,0,0,1<<5
  788. ,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1<<8};
  789.  
  790. long GDColors(GDHandle device)
  791. // Returns the number of colors, in the current mode.
  792. {
  793.     long colors=0;
  794.     short pixelSize;
  795.     
  796.     if(device==NULL)return 2;    // for compatibility with 1-bit QuickDraw
  797.     pixelSize=(**(**device).gdPMap).pixelSize;
  798.     if(pixelSize>=1 && pixelSize<=8)colors=1<<pixelSize;
  799.     if(pixelSize==16)colors=1L<<15;
  800.     if(pixelSize==32)colors=1L<<24;
  801.     return colors;
  802. }
  803.  
  804. short GDClutSize(GDHandle device)
  805. // Returns the number of entries in the clut, in the current mode.
  806. {
  807.     short clutSize;
  808.     
  809.     // Method 1. Estimate clut size from pixel size.
  810.     clutSize=gdClutSizeTable[(**(**device).gdPMap).pixelSize];
  811.  
  812.     #if 0
  813.     // Method 2. Measure the clut's size by trying to load it.
  814.     if(GDType(device)==directType){
  815.         int error,i;
  816.         static const ColorSpec white={255,0xffff,0xffff,0xffff},black={0,0,0,0};
  817.         ColorSpec *table;
  818.         for(clutSize=256;clutSize>1;clutSize>>=1){
  819.             table=GDNewLinearColorTable(device);
  820.             if(table==NULL)PrintfExit("GDClutSize: out of memory.\n");
  821.             error=GDDirectSetEntries(device,0,clutSize-1,table);
  822.             DisposePtr((Ptr)table);
  823.             if(!error)break;
  824.         }
  825.     }
  826.     #endif
  827.     
  828.     return clutSize;
  829. }
  830.  
  831. short GDDacSize(GDHandle device)
  832. // Figures out how many bits in the video dac. Answers for each device are cached.
  833. {
  834.     short dacSize,i;
  835.     static short dacSizeCache[MAX_SCREENS];
  836.     static GDHandle deviceCache[MAX_SCREENS];
  837.     int error;
  838.     GammaTbl *gammaTblPtr=NULL;
  839.  
  840.     for(i=0;i<MAX_SCREENS;i++)if(device==deviceCache[i])return dacSizeCache[i];
  841.     error=GDGetGamma(device,&gammaTblPtr);        // Takes 200 µs.
  842.     if(error || gammaTblPtr==NULL || gammaTblPtr->gDataWidth==0)dacSize=8;    // Oops. Take a guess.
  843.     else dacSize=gammaTblPtr->gDataWidth;
  844.     for(i=0;i<MAX_SCREENS;i++)if(NULL==deviceCache[i]){
  845.         deviceCache[i]=device;
  846.         dacSizeCache[i]=dacSize;
  847.         break;
  848.     }
  849.     return dacSize;
  850. }
  851.  
  852. ColorSpec *GDNewLinearColorTable(GDHandle device)
  853. // Creates a default table for use when gdType==directType.
  854. {
  855.     short clutSize,i;
  856.     ColorSpec *table,*linearTable;
  857.     
  858.     clutSize=GDClutSize(device);
  859.     table=linearTable=(ColorSpec *)NewPtr(clutSize*sizeof(linearTable[0]));
  860.     if(linearTable!=NULL)for(i=0;i<clutSize;i++){
  861.         table->rgb.red=table->rgb.green=table->rgb.blue
  862.             =(0xffffL*i+clutSize/2)/(clutSize-1);
  863.         table++;
  864.     }
  865.     return linearTable;
  866. }
  867.  
  868. /*
  869. Nominally equivalent to Apple's RestoreDeviceClut(), which is only available
  870. since System 6.05. However, I find that Apple's routine sometimes does nothing,
  871. whereas this routine always works. Passing a NULL argument causes restoration of
  872. cluts of all video devices.
  873. */
  874. OSErr GDRestoreDeviceClut(GDHandle device)
  875. {
  876.     int error,lastError;
  877.  
  878.     // If NULL, then call ourselves for each device.
  879.     if(device==NULL){
  880.         lastError=0;
  881.         device=GetDeviceList();
  882.         while(device!=NULL) {
  883.             if(TestDeviceAttribute(device,screenDevice) && TestDeviceAttribute(device,screenActive)){
  884.                 error=GDRestoreDeviceClut(device);
  885.                 if(error)lastError=error;
  886.             }
  887.             device=GetNextDevice(device);
  888.         }
  889.         return lastError;
  890.     }
  891.     if(GDType(device)!=directType){
  892.         error=GDSetEntries(device,0,(**(**(**device).gdPMap).pmTable).ctSize
  893.             ,((**(**(**device).gdPMap).pmTable)).ctTable);
  894.     }else error=GDSetGamma(device,NULL);
  895.     return error;
  896. }
  897.  
  898. GammaTbl **savedGammaTable[MAX_SCREENS]={NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
  899.  
  900. OSErr GDDisposeGamma(GDHandle device)
  901. {
  902.     int error=0,lastError,i;
  903.  
  904.     // If NULL, then call ourselves for each device.
  905.     if(device==NULL){
  906.         lastError=0;
  907.         device=GetDeviceList();
  908.         while(device!=NULL) {
  909.             if(TestDeviceAttribute(device,screenDevice) && TestDeviceAttribute(device,screenActive)){
  910.                 error=GDDisposeGamma(device);
  911.                 if(error)lastError=error;
  912.             }
  913.             device=GetNextDevice(device);
  914.         }
  915.         return lastError;
  916.     }
  917.     if(device==NULL || (*device)->gdType==fixedType)return 0;
  918.     i=GetScreenIndex(device);
  919.     if(i>=MAX_SCREENS)return 1;
  920.     if(savedGammaTable[i]==NULL)return 0;
  921.     DisposeHandle((Handle)savedGammaTable[i]);
  922.     savedGammaTable[i]=NULL;
  923.     return error;
  924. }
  925.  
  926. OSErr GDSaveGamma(GDHandle device)
  927. {
  928.     GammaTbl *gamma;
  929.     int error,lastError;
  930.     long size;
  931.     int i;
  932.  
  933.     // If NULL, then call ourselves for each device.
  934.     if(device==NULL){
  935.         lastError=0;
  936.         device=GetDeviceList();
  937.         while(device!=NULL) {
  938.             if(TestDeviceAttribute(device,screenDevice) && TestDeviceAttribute(device,screenActive)){
  939.                 error=GDSaveGamma(device);
  940.                 if(error)lastError=error;
  941.             }
  942.             device=GetNextDevice(device);
  943.         }
  944.         return lastError;
  945.     }
  946.     if(device==NULL || (*device)->gdType==fixedType)return 0;
  947.     i=GetScreenIndex(device);
  948.     if(i>=MAX_SCREENS)return 1;
  949.     // If it's already been saved, then don't save it again. The idea is that when we're finally
  950.     // done, we'd like to restore the ORIGINAL gamma table. As implemented here, the user can
  951.     // surround each gamma table fiddle with a pair of calls to save and restore, and know that
  952.     // each restore will replace the ORIGINAL gamma table, as it existed before we started fiddling.
  953.     if(savedGammaTable[i]!=NULL)return 0;
  954.      error=GDGetGamma(device,&gamma);
  955.     if(error)return error;
  956.     size=gamma->gChanCnt*gamma->gDataCnt;
  957.     if(gamma->gDataWidth>8)size*=2;
  958.     size+=sizeof(GammaTbl)+gamma->gFormulaSize;
  959.     error=PtrToHand(gamma,(Handle *)&savedGammaTable[i],size);
  960.     return error;
  961. }
  962.  
  963. OSErr GDRestoreGamma(GDHandle device)
  964. {
  965.     int i,error,lastError;
  966.     
  967.     // If NULL, then call ourselves for each device.
  968.     if(device==NULL){
  969.         lastError=0;
  970.         device=GetDeviceList();
  971.         while(device!=NULL) {
  972.             if(TestDeviceAttribute(device,screenDevice) && TestDeviceAttribute(device,screenActive)){
  973.                 error=GDRestoreGamma(device);
  974.                 if(error)lastError=error;
  975.             }
  976.             device=GetNextDevice(device);
  977.         }
  978.         return lastError;
  979.     }
  980.     if((*device)->gdType==fixedType)return 0;
  981.     i=GetScreenIndex(device);
  982.     if(i>=MAX_SCREENS || savedGammaTable[i]==NULL)return 1;
  983.     error=GDSetGamma(device,*savedGammaTable[i]);
  984.     if(error)return error;
  985.     if(0){
  986.         DisposeHandle((Handle)savedGammaTable[i]);
  987.         savedGammaTable[i]=NULL;
  988.     }
  989.     return 0;
  990. }    
  991.  
  992. OSErr GDGetDefaultGamma(GDHandle device,GammaTbl **gammaTbl)
  993. // Looks for a default gamma table in both places that Apple says to look,
  994. // but usually comes up empty handed.
  995. {
  996.     int error,i,slot;
  997.     Scrn **scrn;
  998.     GammaTbl **gammaHandle,*gamma;
  999.     SpBlock spBlock;
  1000.     Ptr ptr;
  1001.  
  1002.     gammaHandle=NULL;
  1003.     if(CountResources('gama')>0){// Any 'gama' resources available in System file?
  1004.         // Check to see if 'scrn' resource in System file specifies a 'gama' resource.
  1005.         scrn=GDGetScrn(device);
  1006.         if(scrn!=NULL && (**scrn).gammaTable!=-1)
  1007.             gammaHandle=(GammaTbl **)GetResource('gama',(**scrn).gammaTable);
  1008.         DisposeHandle((Handle)scrn);
  1009.     }
  1010.     if(gammaHandle!=NULL){
  1011.         // Got gamma table from 'gama' resource.
  1012.         gamma=(GammaTbl *)NewPtr(GetHandleSize((Handle)gammaHandle));
  1013.         if(gamma==NULL)return MemError();
  1014.         BlockMove(*gammaHandle,gamma,GetHandleSize((Handle)gammaHandle));
  1015.         DisposeHandle((Handle)gammaHandle);
  1016.     }else{
  1017.         // Try to get this device's default gamma table from Slot Manager.
  1018.         spBlock.spSlot=slot=GetDeviceSlot(device);
  1019.         spBlock.spID=0;
  1020.         spBlock.spExtDev=0;
  1021.         spBlock.spTBMask=3;            // match only spCategory and spCType
  1022.         spBlock.spCategory=catDisplay;
  1023.         spBlock.spCType=typeVideo;    // this might be too restrictive, excludes LCD
  1024.         do{
  1025.             error=SNextTypeSRsrc(&spBlock);
  1026.             if(error)return error;
  1027.         }while(spBlock.spSlot!=slot || spBlock.spRefNum!=(**device).gdRefNum);
  1028.         
  1029.         if(0){
  1030.             // Print sResource name
  1031.             spBlock.spID=sRsrcName;
  1032.             error=SGetCString(&spBlock);
  1033.             printf("Slot resource \"%s\"\n",spBlock.spResult);
  1034.             if(error)return error;
  1035.             DisposePtr((Ptr)spBlock.spResult);
  1036.         }
  1037.         
  1038.         // Look for gamma directory. Unfortunately many video devices don't have one.
  1039.         spBlock.spID=sGammaDir;
  1040.         error=SFindStruct(&spBlock);
  1041.         if(error)return error;
  1042.         
  1043.         // Retrieve default gamma table
  1044.         spBlock.spID=0x80;
  1045.         error=SGetBlock(&spBlock);
  1046.         if(error)return error;
  1047.         gamma=(GammaTbl *)spBlock.spResult;
  1048.         ptr=(Ptr)spBlock.spResult+6;
  1049.         if(0)printf("Gamma table \"%s\", %ld bytes\n",ptr,GetPtrSize((Ptr)gamma));
  1050.         ptr+=strlen(ptr)+1;                // table is just past string
  1051.         ptr=(Ptr)((long)(ptr+1)&~1L);    // round up to even address
  1052.         i=ptr-(Ptr)gamma;
  1053.         BlockMove(ptr,(Ptr)gamma,GetPtrSize((Ptr)gamma)-i);
  1054.         SetPtrSize((Ptr)gamma,GetPtrSize((Ptr)gamma)-i);
  1055.     }
  1056.     *gammaTbl=gamma;
  1057.     return 0;
  1058. }
  1059.  
  1060. Scrn **GDGetScrn(GDHandle device)
  1061. // Returns handle to a copy of the specific scrn resource for this device,
  1062. // or NULL if none.
  1063. {
  1064.     int error=0,i,j;
  1065.     Scrns **scrns;
  1066.     Scrn *scrn,**scrnHandle;
  1067.     ScrnCtl *ctl;
  1068.     int scrnCount;
  1069.     long size;
  1070.  
  1071.     scrns=(Scrns **)GetResource('scrn',0);
  1072.     if(ResError())return NULL;
  1073.     HLockHi((Handle)scrns);
  1074.     scrnCount=(**scrns).scrnCount;
  1075.     scrn=&(**scrns).scrn;
  1076.     for(i=0;i<scrnCount;i++){
  1077.         if(0 && scrn->slot==GetDeviceSlot(device)){
  1078.             printf("Slot %d,",(int)scrn->slot);
  1079.             printf("mode %d,",(int)scrn->mode);
  1080.             printf("colorTable %d,",(int)scrn->colorTable);
  1081.             printf("gammaTable %d,",(int)scrn->gammaTable);
  1082.             printf("%d control calls:",(int)scrn->ctlCount);
  1083.         }
  1084.         ctl=&scrn->ctl;
  1085.         for(j=0;j<scrn->ctlCount;j++){
  1086.             if(0 && scrn->slot==GetDeviceSlot(device))printf(" %d",(int)ctl->csCode);
  1087.             ctl=(ScrnCtl *)((long)(ctl+1)+ctl->length);
  1088.         }
  1089.         if(0 && scrn->slot==GetDeviceSlot(device))printf("\n");
  1090.         size=(long)ctl-(long)scrn;
  1091.         if(scrn->slot==GetDeviceSlot(device))break;
  1092.         scrn=(Scrn *)ctl;
  1093.     }
  1094.     if(i<scrnCount){
  1095.         if(error)return NULL;
  1096.         scrnHandle=(Scrn **)NewHandle(size);
  1097.         BlockMove(scrn,*scrnHandle,size);
  1098.     }else scrnHandle=NULL;
  1099.     ReleaseResource((Handle)scrns);
  1100.     return scrnHandle;
  1101. }
  1102.  
  1103. OSErr GDUncorrectedGamma(GDHandle device)
  1104. /*
  1105. Loads a linear gamma table into the specified video device, to defeat the
  1106. driver's attempt to do gamma correction.
  1107.  
  1108. According to Designing Cards and Drivers, 3rd edition, passing a NULL
  1109. GammaTblPtr instructs the driver to create a linear table. 
  1110.  
  1111. */
  1112. {
  1113.     int error,i;
  1114.     GammaTbl *gamma=NULL;
  1115.     char *gData;
  1116.  
  1117.     if(0){
  1118.         /*
  1119.         The the following code first examines the current
  1120.         gamma table, and, if it's a plain vanilla table, as described in Designing Cards
  1121.         and Drivers, then we write-in the desired linear table. If it's fancy (or if the
  1122.         driver doesn't support GDGetGamma) then we pass a NULL pointer, letting the driver
  1123.         create the new table. Hopefully this will work with all drivers.
  1124.         */
  1125.         error=GDGetGamma(device,&gamma);
  1126.         if(!error && gamma->gVersion==0 && gamma->gChanCnt==1 && gamma->gDataWidth<=8){
  1127.             // Overwrite the standard table
  1128.             gData = (char *)&gamma->gFormulaData+gamma->gFormulaSize;
  1129.             for(i=0;i<gamma->gDataCnt;i++)gData[i]=i;
  1130.         }else{
  1131.             // A fancy table implies a new driver, so let it do the work.
  1132.             gamma=NULL;
  1133.         }
  1134.     }
  1135.     error=GDSetGamma(device,gamma);
  1136.     return error;
  1137. }
  1138.  
  1139. /* KillIO doesn't do anything, so I didn't bother to implement it. */
  1140.  
  1141. OSErr GDPrintGammaTable(FILE *o,GDHandle device)
  1142. {
  1143.     unsigned char *byte;
  1144.     unsigned short *word;
  1145.     int error,i,j,identity;
  1146.     GammaTbl *gamma;
  1147.     
  1148.     if((**device).gdType==fixedType)return statusErr;
  1149.     error=GDGetGamma(device,&gamma);
  1150.     if(error){
  1151.         fprintf(o,"GetGamma: GDGetGamma() error %d\n",error);
  1152.         if(error==statusErr)
  1153.             fprintf(o,"The video driver doesn't support this call.\n");
  1154.         return error;
  1155.     }
  1156.     byte=(unsigned char *)gamma->gFormulaData+gamma->gFormulaSize;
  1157.     word=(unsigned short *)byte;
  1158.     identity=1;
  1159.     if(gamma->gDataWidth<=8)
  1160.         for(i=0;i<gamma->gDataCnt;i++)identity &= (i==byte[i]);
  1161.     else
  1162.         for(i=0;i<gamma->gDataCnt;i++)identity &= (i==word[i]);
  1163.     if(identity){
  1164.         fprintf(o,"Gamma Table: identity transformation\n");
  1165.     }else{
  1166.         fprintf(o,"Gamma Table:\n");
  1167.         fprintf(o,"at 0x%lx,gDataWidth %d,gDataCnt %d,gVersion %d,gType %d,gFormulaSize %d,gChanCnt %d\n"
  1168.             ,gamma,(int)gamma->gDataWidth,(int)gamma->gDataCnt,(int)gamma->gVersion
  1169.             ,(int)gamma->gType,(int)gamma->gFormulaSize,(int)gamma->gChanCnt);
  1170.         for(i=0;i<gamma->gDataCnt;i+=64) {
  1171.             fprintf(o,"%3d: ",i);
  1172.             if(gamma->gDataWidth<=8)
  1173.                 for(j=0;j<16;j++) fprintf(o," %3u",byte[i+j]);
  1174.             else
  1175.                 for(j=0;j<16;j++) fprintf(o," %3u",word[i+j]);
  1176.             fprintf(o,"\n");
  1177.         }
  1178.     }
  1179.     return 0;
  1180. }
  1181.  
  1182. OSErr GDReset(GDHandle device, short *modePtr, short *pagePtr, Ptr *baseAddrPtr)
  1183. /*
  1184. Initialize the video card to its startup state, usually 1 bit per pixel. Returns
  1185. the parameters of that state.
  1186. */
  1187. {
  1188.     VDPageInfo myVDPageInfo;
  1189.     int error;
  1190.  
  1191.     if(device==NULL || (*device)->gdRefNum==0) return controlErr;
  1192.     myVDPageInfo.csMode= *modePtr;
  1193.     myVDPageInfo.csPage= *pagePtr;
  1194.     error=GDControl((*device)->gdRefNum,cscReset,(Ptr) &myVDPageInfo);
  1195.     *modePtr=myVDPageInfo.csMode;
  1196.     *pagePtr=myVDPageInfo.csPage;
  1197.     if(baseAddrPtr!=NULL) *baseAddrPtr=myVDPageInfo.csBaseAddr;
  1198.     return error;
  1199. }
  1200.  
  1201. OSErr GDSetMode(GDHandle device,short mode,short page,Ptr *baseAddrPtr)
  1202. {
  1203.     VDPageInfo myVDPageInfo;
  1204.     int error;
  1205.  
  1206.     if(device==NULL || (*device)->gdRefNum==0) return controlErr;
  1207.     myVDPageInfo.csMode=mode;
  1208.     myVDPageInfo.csPage=page;
  1209.     error=GDControl((*device)->gdRefNum,cscSetMode,(Ptr) &myVDPageInfo);
  1210.     if(baseAddrPtr!=NULL) *baseAddrPtr=myVDPageInfo.csBaseAddr;
  1211.     return error;
  1212. }
  1213.  
  1214. OSErr GDSwitchMode(GDHandle device,short mode,long displayModeID,short page,Ptr *baseAddrPtr)
  1215. /* cscSwitchMode is documented in Designing PCI Video Cards and Drivers. */
  1216. {
  1217.     VDSwitchInfoRec vdSwitchInfo;
  1218.     int error;
  1219.  
  1220.     if(device==NULL || (*device)->gdRefNum==0) return controlErr;
  1221.     vdSwitchInfo.csMode=mode;
  1222.     vdSwitchInfo.csData=displayModeID;
  1223.     vdSwitchInfo.csPage=page;
  1224.     error=GDControl((*device)->gdRefNum,cscSwitchMode,(Ptr) &vdSwitchInfo);
  1225.     if(baseAddrPtr!=NULL) *baseAddrPtr=vdSwitchInfo.csBaseAddr;
  1226.     return error;
  1227. }
  1228.  
  1229. OSErr GDSetEntriesByType(GDHandle device,short start,short count,ColorSpec *table)
  1230. // Calls GDSetEntries or GDDirectSetEntries or nothing, as appropriate.
  1231. // Assumes that the GDevice record is valid, i.e. that the user has not
  1232. // called GDSetMode.
  1233. {
  1234.     switch((*device)->gdType){
  1235.     case fixedType:
  1236.         return statusErr;
  1237.     case clutType:
  1238.         return GDSetEntries(device,start,count,table);
  1239.     case directType:
  1240.         return GDDirectSetEntries(device,start,count,table);
  1241.     }
  1242.     return 1;
  1243. }
  1244.  
  1245. OSErr GDSetEntriesByTypeHighPriority(GDHandle device,short start,short count
  1246.     ,ColorSpec *table)
  1247. {
  1248.     char priority;
  1249.     int error;
  1250.  
  1251.     priority=7;
  1252.     SwapPriority(&priority);
  1253.     error=GDSetEntriesByType(device,start,count,table);
  1254.     SwapPriority(&priority);
  1255.     return error;
  1256. }
  1257.  
  1258. OSErr GDSetEntries(GDHandle device,short start,short count,ColorSpec *table)
  1259. // Note that cscSetEntries reads from table[0],.... 
  1260. // whereas cscGetEntries writes to table[start],.... 
  1261. {
  1262.     VDSetEntryRecord vDSetEntryRecord;
  1263.     int error;
  1264.  
  1265.     if(device==NULL || (*device)->gdRefNum==0) return controlErr;
  1266.     vDSetEntryRecord.csStart=start;
  1267.     vDSetEntryRecord.csCount=count;
  1268.     vDSetEntryRecord.csTable=table;
  1269.     error=GDControl((*device)->gdRefNum,cscSetEntries,(Ptr) &vDSetEntryRecord);
  1270.     return error;
  1271. }
  1272.  
  1273. OSErr GDSetGamma(GDHandle device, GammaTbl *gamma)
  1274. {
  1275.     int error;
  1276.     VDGammaRecord myVDGammaRecord;
  1277.  
  1278.     myVDGammaRecord.csGTable=(Ptr)gamma;
  1279.     error=GDControl((*device)->gdRefNum,cscSetGamma,(Ptr) &myVDGammaRecord);
  1280.     return error;
  1281. }
  1282.  
  1283. OSErr GDGrayPage(GDHandle device,short page)
  1284. /*
  1285. Called "GrayScreen" in Designing Cards and Drivers, 3rd Ed. Fills the specified
  1286. page with gray, i.e. the dithered desktop pattern. I'm not aware of any
  1287. particular advantage in using this instead of FillRect().
  1288.  
  1289. Designing Cards and Drivers, 3rd Edition, Chapter 9, says that for direct
  1290. devices, i.e. >8 bit pixels, the driver will also load a linear,
  1291. gamma-corrected, gray color table.
  1292.  
  1293. Contrary to the documentation, version 2 (in System 6.03) of the video driver
  1294. for Apple's old video card requires that one supply the current mode as well.
  1295. Supplying a garbage mode screwed up the screen and soon hung the software. So
  1296. this code first obtains the current mode, and then supplies it in the GrayPage
  1297. Control call.
  1298. */
  1299. {
  1300.     VDPageInfo myVDPageInfo;
  1301.     int error;
  1302.     /* The rest of the arguments are used soley for the bug fix */
  1303.     short mode=0;        /* should be ignored, but isn't */
  1304.     short actualPage;    /* ignored */
  1305.     Ptr baseAddr;        /* ignored */
  1306.  
  1307.     if(device==NULL || (*device)->gdRefNum==0) return controlErr;
  1308.     
  1309.     /* Work around driver bug: get the mode */
  1310.     error=GDGetMode(device,&mode,&actualPage,&baseAddr);
  1311.     if(error)return error;
  1312.     myVDPageInfo.csMode=mode;
  1313.     
  1314.     myVDPageInfo.csPage=page;
  1315.     error=GDControl((*device)->gdRefNum,cscGrayPage,(Ptr) &myVDPageInfo);
  1316.     return error;
  1317. }
  1318.  
  1319. OSErr GDSetGray(GDHandle device,Boolean flag)
  1320. /*
  1321. Tells the driver whether you want colors (flag==0), or prefer that all colors be 
  1322. mapped to luminance-equivalent gray tones? (flag==1).
  1323. */
  1324. {
  1325.     VDFlagRec myVDFlagRec;
  1326.     int error;
  1327.  
  1328.     if(device==NULL || (*device)->gdRefNum==0) return controlErr;
  1329.     myVDFlagRec.flag=flag;
  1330.     error=GDControl((*device)->gdRefNum,cscSetGray,(Ptr) &myVDFlagRec);
  1331.     return error;
  1332. }
  1333.  
  1334. OSErr GDSetInterrupt(GDHandle device,Boolean flag)
  1335. /*
  1336. Set flag to 1 to enable VBL interrupts of this card, or 0 to disable. 
  1337. I don't know when it's appropriate to call this.
  1338. */
  1339. {
  1340.     VDFlagRec myVDFlagRec;
  1341.     int error;
  1342.  
  1343.     if(device==NULL || (*device)->gdRefNum==0) return controlErr;
  1344.     myVDFlagRec.flag=flag;
  1345.     error=GDControl((*device)->gdRefNum,cscSetInterrupt,(Ptr) &myVDFlagRec);
  1346.     return error;
  1347. }
  1348.  
  1349. OSErr GDDirectSetEntries(GDHandle device,short start,short count,ColorSpec *table)
  1350. /*
  1351. If your pixel depth is >8 then the cscSetEntries Control call is disabled, and you use
  1352. cscDirectSetEntries instead. Except for that, GDDirectSetEntries is identical to GDSetEntries.
  1353. */
  1354. {
  1355.     VDSetEntryRecord vDSetEntryRecord;
  1356.     int error;
  1357.  
  1358.     if(device==NULL || (*device)->gdRefNum==0) return controlErr;
  1359.     vDSetEntryRecord.csStart=start;
  1360.     vDSetEntryRecord.csCount=count;
  1361.     vDSetEntryRecord.csTable=table;
  1362.     error=GDControl((*device)->gdRefNum,cscDirectSetEntries,(Ptr) &vDSetEntryRecord);
  1363.     return error;
  1364. }
  1365.  
  1366. OSErr GDSetDefaultMode(GDHandle device,short mode)
  1367. /*
  1368. Supposedly, you tell it what mode you want to start up with when you reboot.
  1369. (I've never been able to get this to work. No error and no effect. Perhaps I've
  1370. misunderstood its purpose.)
  1371. */
  1372. {
  1373.     VDDefModeRec myVDDefModeRec;
  1374.     int error;
  1375.  
  1376.     if(device==NULL || (*device)->gdRefNum==0) return controlErr;
  1377.     myVDDefModeRec.spID=mode;
  1378.     error=GDControl((*device)->gdRefNum,cscSetDefaultMode,(Ptr) &myVDDefModeRec);
  1379.     return error;
  1380. }
  1381.  
  1382. OSErr GDGetMode(GDHandle device,short *modePtr,short *pagePtr,Ptr *baseAddrPtr)
  1383. /*
  1384. It tells you the current mode, page of video memory, and the base address of that
  1385. page.
  1386. */
  1387. {
  1388.     VDPageInfo myVDPageInfo;
  1389.     int error;
  1390.  
  1391.     if(device==NULL || (*device)->gdRefNum==0) return statusErr;
  1392.     error=GDStatus((*device)->gdRefNum,cscGetMode,(Ptr) &myVDPageInfo);
  1393.     if(modePtr!=NULL)*modePtr=myVDPageInfo.csMode;
  1394.     if(pagePtr!=NULL)*pagePtr=myVDPageInfo.csPage;
  1395.     if(baseAddrPtr!=NULL) *baseAddrPtr=myVDPageInfo.csBaseAddr;
  1396.     return error;
  1397. }
  1398.  
  1399. OSErr GDGetEntries(GDHandle device,short start,short count,ColorSpec *table)
  1400. // Note that cscSetEntries reads from table[0],... 
  1401. // whereas cscGetEntries writes to table[start],...
  1402. /*
  1403. This is much as you'd expect after reading GDSetEntries above. Note that unless
  1404. the gamma table is linear, the values returned may not be the same as those
  1405. originally passed by GDSetEntries. So call GDUncorrectedGamma first. Note that
  1406. version 2 (in System 6.03) of the video driver for Apple's old video card had a
  1407. bug and did not support "indexed" mode, i.e. start==-1. This is fixed in System
  1408. 6.05. Apple's .Display_Video_Apple_RBV1 v. 0 video driver (built-in to Mac IIci)
  1409. crashes if you attempt to make this call. However, that's a thing of the past,
  1410. because we now first patch the driver to fix the bug. Try the demo TimeVideo.
  1411. */
  1412. {
  1413.     VDSetEntryRecord vDSetEntryRecord;
  1414.     int error;
  1415.     static Boolean bugsPatched=0;
  1416.     unsigned char *name;
  1417.     int version;
  1418.  
  1419.     if(device==NULL || (*device)->gdRefNum==0)return statusErr;
  1420.     if(!bugsPatched){
  1421.         PatchMacIIciVideoDriver();
  1422.         bugsPatched=1;
  1423.     }
  1424.  
  1425.     // Contrary to Apple's rules, these drivers crash if we attempt
  1426.     // a cscGetEntries call. So let's be polite and instead simply return
  1427.     // an error message indicating that this call is not available.
  1428.     name=GDName(device);
  1429.     version=GDVersion(device);
  1430.     if(EqualString(name,"\p.Display_Video_Apple_RBV1",1,1) && version==0)return statusErr;
  1431.     if(EqualString(name,"\p.Color_Video_Display",1,1) && version==9288)return statusErr;
  1432.     if(EqualString(name,"\p.Display_Video_Apple_DOMEMax",1,1) && version==2)return statusErr;
  1433.  
  1434.     vDSetEntryRecord.csStart=start;
  1435.     vDSetEntryRecord.csCount=count;
  1436.     vDSetEntryRecord.csTable=table;
  1437.     error=GDStatus((*device)->gdRefNum,cscGetEntries,(Ptr) &vDSetEntryRecord);
  1438.     return error;
  1439. }
  1440.  
  1441. OSErr GDGetPageCnt(GDHandle device,short mode,short *pagesPtr)
  1442. /*
  1443. Called "GetPages" in Designing Cards and Drivers, 3rd Ed. You tell it what mode
  1444. you're interested in. It tells you how many pages of video ram are available.
  1445. */
  1446. {
  1447.     VDPageInfo myVDPageInfo;
  1448.     int error;
  1449.  
  1450.     if(device==NULL || (*device)->gdRefNum==0) return statusErr;
  1451.     myVDPageInfo.csMode=mode;
  1452.     error=GDStatus((*device)->gdRefNum,cscGetPageCnt,(Ptr) &myVDPageInfo);
  1453.     if(!error)*pagesPtr=myVDPageInfo.csPage;
  1454.     return error;
  1455. }
  1456.  
  1457. OSErr GDGetPageBase(GDHandle device,short page,Ptr *baseAddrPtr)
  1458. /*
  1459. Called "GetBaseAddr" in Designing Cards and Drivers, 3rd Ed. You tell it what
  1460. page of video memory you're interested in (in the current video mode). It tells
  1461. you the base address of that page.
  1462. */
  1463. {
  1464.     VDPageInfo myVDPageInfo;
  1465.     int error;
  1466.  
  1467.     if(device==NULL || (*device)->gdRefNum==0) return statusErr;
  1468.     myVDPageInfo.csPage=page;
  1469.     error=GDStatus((*device)->gdRefNum,cscGetPageBase,(Ptr) &myVDPageInfo);
  1470.     if(baseAddrPtr!=NULL) *baseAddrPtr=myVDPageInfo.csBaseAddr;
  1471.     return error;
  1472. }
  1473.  
  1474. OSErr GDGetGray(GDHandle device,Boolean *flagPtr)
  1475. // It tells you what the flag is set to. Do you want colors? (flag==0) Or do you
  1476. // want all colors mapped to luminance-equivalent gray tones? (flag==1).
  1477. {
  1478.     VDFlagRec myVDFlagRec;
  1479.     int error;
  1480.  
  1481.     if(device==NULL || (*device)->gdRefNum==0) return statusErr;
  1482.     error=GDStatus((*device)->gdRefNum,cscGetGray,(Ptr) &myVDFlagRec);
  1483.     *flagPtr=myVDFlagRec.flag;
  1484.     return error;
  1485. }
  1486.  
  1487. OSErr GDGetInterrupt(GDHandle device,Boolean *flagPtr)
  1488. // Get flag. 1 if VBL interrupts of this card are enabled. 0 if disabled. 
  1489. {
  1490.     VDFlagRec myVDFlagRec;
  1491.     int error;
  1492.  
  1493.     if(device==NULL || (*device)->gdRefNum==0) return statusErr;
  1494.     error=GDStatus((*device)->gdRefNum,cscGetInterrupt,(Ptr) &myVDFlagRec);
  1495.     *flagPtr=myVDFlagRec.flag;
  1496.     return error;
  1497. }
  1498.  
  1499. OSErr GDGetGamma(GDHandle device,GammaTbl **myGammaTblHandle)
  1500. /*
  1501. Returns a pointer to the Gamma table in the specified video device. (I.e. you
  1502. pass it a pointer to your pointer, a handle, which it uses to load your
  1503. pointer.)
  1504.  
  1505. Note that version 2 (in System ≤6.03) of the video driver for Apple's old video
  1506. card does not support this call due to a bug in the driver code. The later
  1507. versions of the driver (3, 4, and 5, in System 6.04 and later) work correctly.
  1508. */
  1509. {
  1510.     int error;
  1511.     VDGammaRecord myVDGammaRecord;
  1512.  
  1513.     if(device==NULL || (*device)->gdRefNum==0) return statusErr;
  1514.     myVDGammaRecord.csGTable=NULL;    // default address is NULL
  1515.     error=GDStatus((*device)->gdRefNum,cscGetGamma,(Ptr) &myVDGammaRecord);
  1516.     *myGammaTblHandle=(GammaTblPtr)myVDGammaRecord.csGTable;
  1517.     return error;
  1518. }
  1519.  
  1520. OSErr GDGetDefaultMode(GDHandle device,short *modePtr)
  1521. // It tells you what the default mode is. I'm not sure what this means.
  1522. {
  1523.     VDDefModeRec myVDDefModeRec;
  1524.     int error;
  1525.  
  1526.     if(device==NULL || (*device)->gdRefNum==0) return statusErr;
  1527.     error=GDStatus((*device)->gdRefNum,cscGetDefaultMode,(Ptr) &myVDDefModeRec);
  1528.     *modePtr=(unsigned char)myVDDefModeRec.spID;
  1529.     return error;
  1530. }
  1531.  
  1532. OSErr GDControl(int refNum,int csCode,Ptr csParamPtr)
  1533. // Uses low-level PBControl() call to implement a "Control()" call that works! 
  1534. // I don't know why this wasn't discussed in Apple Tech Note 262.
  1535. {
  1536.     CntrlParam control;
  1537.     int error;
  1538.     
  1539.     control.ioCompletion=NULL;
  1540.     control.ioCRefNum=refNum;
  1541.     control.csCode=csCode;
  1542.     *((Ptr *) &control.csParam[0]) = csParamPtr;
  1543.     error=PBControl((ParmBlkPtr) &control,0);
  1544.     return error;
  1545. }
  1546.  
  1547. OSErr GDStatus(int refNum,int csCode,Ptr csParamPtr)
  1548. // Uses low-level PBStatus() call to implement a "Status()" call that works! The
  1549. // need for this is explained in Apple Tech Note 262, which was issued in response
  1550. // to my bug report in summer of '89.
  1551. {
  1552.     CntrlParam control;
  1553.     int error;
  1554.     
  1555.     control.ioCompletion=NULL;
  1556.     control.ioCRefNum=refNum;
  1557.     control.csCode=csCode;
  1558.     *((Ptr *) &control.csParam[0]) = csParamPtr;
  1559.     error=PBStatus((ParmBlkPtr) &control,0);
  1560.     return error;
  1561. }
  1562.  
  1563. #if 0
  1564. /*
  1565. From: absurd@apple.apple.com (Tim Dierks, software saboteur)
  1566. Date: Fri, 20 Nov 1992 00:38:14 GMT
  1567. Organization: MacDTS Marauders
  1568.  
  1569. Here's the right way to get the slot number of a monitor, given its
  1570. GDevice:  (as a bonus, this also gets the name of the card in
  1571. question; to just get the slot, chop off all the lines after the
  1572. *slot = ... line.
  1573.  
  1574. 10/4/95 dgp added safety checks at beginning to make sure device handle is
  1575. not NULL and that the Slot Manager is present (e.g. a PCI machine).
  1576. */
  1577.  
  1578. OSErr GetSlotAndName(GDHandle device,short *slot,char *name)
  1579. {   OSErr error;
  1580.     SpBlock spBlock;
  1581.     Boolean slotMgrPresent;
  1582.     
  1583.     slot=-1;
  1584.     name[0]=0;
  1585.     if(TrapAvailable(_SlotManager))slotMgrPresent=(SVersion(&spBlock)==noErr);
  1586.     else slotMgrPresent=0;
  1587.     if(!slotMgrPresent || device==NULL || (*device)->gdRefNum==0)return -1;
  1588.     *slot = (**(AuxDCEHandle)GetDCtlEntry((**device).gdRefNum)).dCtlSlot;
  1589.     spBlock.spSlot = *slot;         // In the slot we're interested in
  1590.     spBlock.spID = 0;
  1591.     spBlock.spExtDev = 0;
  1592.     spBlock.spCategory = 1;         // Get the board sResource
  1593.     spBlock.spCType = 0;
  1594.     spBlock.spDrvrSW = 0;
  1595.     spBlock.spDrvrHW = 0;
  1596.     spBlock.spTBMask = 0;
  1597.     error = SNextTypeSRsrc(&spBlock);
  1598.     if(error)return error;
  1599.     spBlock.spID = 2;               // Getting the description string
  1600.                                     // spSPointer was set up by SNextTypesResource
  1601.     error = SGetCString(&spBlock);
  1602.     if(error)return error;
  1603.     strcpy(name,(char *)spBlock.spResult);  // Get the returned string
  1604.     DisposePtr((Ptr)spBlock.spResult);    // Undocumented; we have to dispose of it
  1605.     c2pstr(name);
  1606.     return noErr;
  1607. }
  1608. #endif
  1609.  
  1610. char *GDCardName(GDHandle device)
  1611. /*
  1612. Returns the address of a C string containing the name of the video card. You
  1613. should call DisposPtr() on the returned address when you no longer need it.
  1614. Takes about 1.5 ms; I don't know why Apple's slot routines are so slow. This
  1615. routine sets up the flags in the SNextTypeSRsrc() call quite differently from
  1616. the above example, but I don't know if that matters, since I've been using this
  1617. routine for a year or so without any problems.
  1618.  
  1619. Now works for PCI video devices as well.
  1620. */
  1621. {
  1622.     AuxDCE **auxDCEHandle;
  1623.     SpBlock spBlock,spBlock1;
  1624.     Boolean slotMgrPresent;
  1625.     char slotName[32],cardName[32],modelName[32];
  1626.     
  1627.     GetVideoProperties(device,slotName,cardName,modelName);    /* in Identify.c */
  1628.     if(strlen(cardName)>0){
  1629.         Ptr p;
  1630.         if(strlen(slotName)==0)strcpy(cardName,"Built-in video");
  1631.         p=NewPtr(strlen(cardName)+1);
  1632.         strcpy(p,cardName);
  1633.         return p;
  1634.     }
  1635.     if(TrapAvailable(_SlotManager))slotMgrPresent=(SVersion(&spBlock)==noErr);
  1636.     else slotMgrPresent=0;
  1637.     if(!slotMgrPresent || device==NULL || (*device)->gdRefNum==0){
  1638.         Ptr p;
  1639.         p=NewPtr(1);
  1640.         p[0]=0;
  1641.         return p;
  1642.     }
  1643.     auxDCEHandle = (AuxDCE **) GetDCtlEntry((*device)->gdRefNum);
  1644.     spBlock.spSlot = (**auxDCEHandle).dCtlSlot;
  1645.     spBlock.spCategory = 0;
  1646.     spBlock.spCType =     0;
  1647.     spBlock.spDrvrSW =     0;
  1648.     spBlock.spDrvrHW =     1;
  1649.     spBlock.spTBMask = 0xf;
  1650.     spBlock.spID = 0;
  1651.     spBlock.spExtDev = 0;
  1652.     if(SNextTypeSRsrc(&spBlock)==noErr) {
  1653.         spBlock1.spsPointer = spBlock.spsPointer;
  1654.         spBlock1.spID = 2;
  1655.         SGetCString(&spBlock1);
  1656.         return (char *)spBlock1.spResult;
  1657.     }
  1658.     else{
  1659.         Ptr p;
  1660.         p=NewPtr(1);
  1661.         p[0]=0;
  1662.         return p;
  1663.     }
  1664. }
  1665.  
  1666. //#include <NameRegistry.h>
  1667. //#include <Drivers.h>
  1668.  
  1669. OSErr GDGestalt(GDHandle device,OSType driverGestaltSelector,unsigned long *driverGestaltResponsePtr)
  1670. {
  1671.     int error;
  1672.  
  1673.     if(device==NULL || (*device)->gdRefNum==0) return statusErr;
  1674.     error=DriverGestalt((*device)->gdRefNum,driverGestaltSelector,driverGestaltResponsePtr);
  1675.     return error;
  1676. }
  1677.  
  1678. struct DriverGestaltParam {
  1679.     QElemPtr qLink;
  1680.     short qType;
  1681.     short ioTrap;
  1682.     Ptr ioCmdAddr;
  1683.     ProcPtr ioCompletion;
  1684.     OSErr ioResult;
  1685.     StringPtr ioNamePtr;
  1686.     short ioVRefNum;
  1687.     short ioCRefNum; /* refNum for I/O operation*/
  1688.     short csCode; /* == driverGestaltCode */
  1689.     OSType driverGestaltSelector;
  1690.     unsigned long driverGestaltResponse;
  1691. };
  1692. typedef struct DriverGestaltParam DriverGestaltParam;
  1693. enum{kcsDriverGestalt=43};
  1694. enum{kmDriverGestaltEnableMask=1L<<2};
  1695.  
  1696. Boolean GDGestaltIsOn(GDHandle device)
  1697. {
  1698.     AuxDCE **auxDCEHandle;
  1699.  
  1700.     if(device==NULL || (*device)->gdRefNum==0) return 0;
  1701.     auxDCEHandle=(AuxDCE **)GetDCtlEntry((*device)->gdRefNum);
  1702.     return (**auxDCEHandle).dCtlFlags & kmDriverGestaltEnableMask;
  1703. }
  1704.  
  1705. OSErr DriverGestalt(int refNum,OSType driverGestaltSelector,unsigned long *driverGestaltResponsePtr)
  1706. /*
  1707. Implements Driver Gestalt, documented in Designing PCI Cards and Drivers
  1708. */
  1709. {
  1710.     DriverGestaltParam param;
  1711.     int error;
  1712.     AuxDCE **auxDCEHandle;
  1713.  
  1714.     auxDCEHandle=(AuxDCE **)GetDCtlEntry(refNum);
  1715.     if(!((**auxDCEHandle).dCtlFlags & kmDriverGestaltEnableMask))return statusErr;
  1716.     param.ioCompletion=NULL;
  1717.     param.ioCRefNum=refNum;
  1718.     param.csCode=kcsDriverGestalt;
  1719.     param.driverGestaltSelector=driverGestaltSelector;
  1720.     param.driverGestaltResponse=0;
  1721.     error=PBStatus((ParmBlkPtr) ¶m,0);
  1722.     if(driverGestaltResponsePtr!=NULL) *driverGestaltResponsePtr=param.driverGestaltResponse;
  1723.     return error;
  1724. }
  1725.  
  1726. #if 0
  1727.     struct DriverType {
  1728.         Str31 nameInfoStr;
  1729.         NumVersion version;
  1730.     }
  1731.     typedef UInt32 DeviceTypeMember;
  1732.     typedef struct DriverType DriverType;
  1733.     typedef struct DriverType *DriverTypePtr;
  1734.     struct DriverOSRuntime {
  1735.     RuntimeOptions driverRuntime;
  1736.     Str31 driverName;
  1737.     UInt32 driverDescReserved[8];
  1738.     };
  1739.     typedef OptionBits RuntimeOptions;
  1740.     typedef struct DriverOSRuntime DriverOSRuntime;
  1741.     
  1742.     struct DriverDescription {
  1743.         OSType driverDescSignature;
  1744.         DriverDescVersion driverDescVersion;
  1745.         DriverType driverType;
  1746.         DriverOSRuntime driverOSRuntimeInfo;
  1747.         DriverOSService driverServices;
  1748.     };
  1749.     typedef struct DriverDescription DriverDescription;
  1750.     typedef struct DriverDescription *DriverDescriptionPtr;
  1751.  
  1752.     OSErr GetDriverName(GDHandle device,unsigned char *name);
  1753.     
  1754.     OSErr GetDriverName(GDHandle device,unsigned char *name)
  1755.     {
  1756.         long nameRegistryVersion;
  1757.         OSErr error;
  1758.         UnitNumber unit;
  1759.         DriverFlags flags;
  1760.         FSSpec driverFileSpec;
  1761.         RegEntryID regEntryID;
  1762.         CFragHFSLocator loc;
  1763.         CFragConnectionID fragmentConnID;
  1764.         DriverOpenCount openCount;
  1765.         DriverEntryPointPtr fragmentMain;
  1766.         DriverDescription driverDescription;
  1767.     
  1768.         name[0]=0;
  1769.         error=Gestalt('nreg',&nameRegistryVersion);
  1770.         if(!error){
  1771.             loc.u.onDisk.fileSpec = &driverFileSpec;
  1772.             error=GetDriverInformation((*device)->gdRefNum,&unit,&flags,&openCount,name
  1773.                 ,®EntryID,&loc,&fragmentConnID,&fragmentMain,&driverDescription);
  1774.         }
  1775.         return error;
  1776.     }
  1777. #endif
  1778.  
  1779. unsigned char *GDName(GDHandle device)
  1780. // Returns a pointer to the name of the driver (a pascal string). 
  1781. {
  1782.     VideoDriver *videoDriverPtr;
  1783.  
  1784.     if(device==NULL || (*device)->gdRefNum==0)return "\p";
  1785.     videoDriverPtr=GDDriverAddress(device);
  1786.     return videoDriverPtr->name;
  1787. }
  1788.  
  1789. char *GDNameStr(GDHandle device)
  1790. // Returns the driver name as a C string.
  1791. {
  1792.     unsigned char *sp;
  1793.     static char name[32];
  1794.     
  1795.     sp=GDName(device);
  1796.     memcpy(name,sp,sp[0]+1);
  1797.     return p2cstr((unsigned char *)name);
  1798. }
  1799.  
  1800. int GDVersion(GDHandle device)
  1801. // Returns the version number of the driver. From the first word-aligned word after
  1802. // the name string.
  1803. {
  1804.     int version;
  1805.     unsigned char *bytePtr;
  1806.  
  1807.     if(device==NULL || (*device)->gdRefNum==0)return 0;
  1808.     bytePtr=GDName(device);
  1809.     bytePtr += 1+bytePtr[0];    /* go to end of Pascal string */
  1810.     bytePtr = (unsigned char *)((long)(bytePtr+1) & ~1);    // round up to word boundary
  1811.     version = *(short *)bytePtr;
  1812.     return version;
  1813. }
  1814.  
  1815. VideoDriver *GDDriverAddress(GDHandle device)
  1816. // Returns a pointer to the driver, whether in ROM or RAM. 
  1817. {
  1818.     AuxDCE **auxDCEHandle;
  1819.     VideoDriver *videoDriverPtr;
  1820.  
  1821.     if(device==NULL || (*device)->gdRefNum==0)return NULL;
  1822.     auxDCEHandle = (AuxDCE **) GetDCtlEntry((*device)->gdRefNum);
  1823.     if((**auxDCEHandle).dCtlFlags & dRAMBasedMask){
  1824.         /* RAM-based driver. */
  1825.         videoDriverPtr=*(VideoDriver **) (**auxDCEHandle).dCtlDriver;
  1826.     }
  1827.     else{
  1828.         /* ROM-based driver. */
  1829.         videoDriverPtr=(VideoDriver *) (**auxDCEHandle).dCtlDriver;
  1830.     }
  1831.     return videoDriverPtr;
  1832. }
  1833.  
  1834. /*
  1835. ROUTINE: PatchMacIIciVideoDriver
  1836. PURPOSE:
  1837. It is unlikely that you will need to call this explicitly, because it is called
  1838. automatically by GDGetEntries the first time it is invoked, and the sole purpose
  1839. of this routine is to fix a driver bug that would cause a crash when called by
  1840. GDGetEntries.
  1841.  
  1842. The Mac IIci built-in video driver (.Display_Video_Apple_RBV1 driver, version 0)
  1843. has a bug that causes it to crash if you try to do a cscGetEntries Status request.
  1844. PatchMacIIciVideoDriver() will find and patch the memory-resident copy of the
  1845. buggy driver. Only two instructions are modified, to save and restore more
  1846. registers. This fix persists only until the next reboot. If the patch is
  1847. successfully applied the version number is increased from 0 to 100, to
  1848. distinguish it from versions 0 and 1.
  1849.  
  1850. A returned value of 1 indicates success: the needed patch was applied, either now
  1851. or previously. A return value of 0 indicates no patch was needed.
  1852.  
  1853. This patch is based on a comparison of the version 0 and 1 drivers used by the
  1854. Mac IIci and IIsi, respectively. The patch changes a pair of save and restore
  1855. operations (MOVEM.L to and from the stack) to save and restore registers D1 and
  1856. A1 as well as D4, A3, and A4. There are many other differences between versions
  1857. 0 and 1 of the driver, but this change is enough to keep the version 0 driver
  1858. from crashing when we attempt to read the clut by calling GDGetEntries.
  1859.  
  1860. The only change that the Mac operating system could possibly notice is that,
  1861. when we're done patching, we set the handle to be non-purgeable, since purging
  1862. and reloading the driver would eliminate the patch.
  1863.  
  1864. edward_de_Jong@bmug.org and Robert Savoy (SAVOY@RISVAX.ROWLAND.ORG) reported
  1865. that their Mac IIci computers' built-in video driver is ROM-based, so
  1866. PatchMacIIciVideoDriver was enhanced to deal with a ROM-based driver, by copying
  1867. the driver into RAM, making that the active driver, and patching it. Robert
  1868. Savoy reports that it works fine.
  1869.  
  1870. An alternative, permanent, solution is described in the file "Video synch":
  1871. upgrading to Apple's bug-free version 1 of the driver. However, that solution
  1872. only works if the Mac IIci has more than one monitor.
  1873.  
  1874. */
  1875.  
  1876. int PatchMacIIciVideoDriver(void)
  1877. {
  1878.     GDHandle device;
  1879.     short *w;
  1880.     AuxDCE **auxDCEHandle;
  1881.     Handle handle;
  1882.     enum{badVersion=0};
  1883.     int error;
  1884.     long value;
  1885.     
  1886.     error=Gestalt(gestaltQuickdrawVersion,&value);
  1887.     if(error || value<gestalt8BitQD)return 0;    // need 8-bit quickdraw
  1888.     device = GetDeviceList();
  1889.     while(1) {
  1890.         if(device==NULL || (*device)->gdRefNum==0)return 0;
  1891.         if (!TestDeviceAttribute(device,screenDevice)
  1892.             || !TestDeviceAttribute(device,screenActive)){
  1893.             device=GetNextDevice(device);
  1894.             continue;
  1895.         }
  1896.         if(EqualString("\p.Display_Video_Apple_RBV1",GDName(device),1,1))
  1897.             switch(GDVersion(device)){
  1898.                 case badVersion:
  1899.                     break;
  1900.                 case badVersion+100:    // already patched
  1901.                     return 1;
  1902.                 default:
  1903.                     return 0;
  1904.         }else{
  1905.             device=GetNextDevice(device);
  1906.             continue;
  1907.         }
  1908.         break;
  1909.     }
  1910.     auxDCEHandle = (AuxDCE **) GetDCtlEntry((*device)->gdRefNum);
  1911.     
  1912.     // Move ROM-based driver into RAM.
  1913.     if(!((**auxDCEHandle).dCtlFlags & dRAMBasedMask)){
  1914.         long bytes;
  1915.         VideoDriver *driver;
  1916.         
  1917.         driver=(VideoDriver *)(**auxDCEHandle).dCtlDriver;
  1918.         // Sometimes the word preceding the driver in ROM seems to be the
  1919.         // driver size, but not always, e.g. the built-in driver on the Mac IIsi.
  1920.         bytes=*((short *)driver-1);
  1921.         // Driver size unknown, guessing (generously) at twice the highest offset.
  1922.         bytes=driver->open;
  1923.         if(bytes<driver->prime)bytes=driver->prime;
  1924.         if(bytes<driver->control)bytes=driver->control;
  1925.         if(bytes<driver->status)bytes=driver->status;
  1926.         if(bytes<driver->close)bytes=driver->close;
  1927.         bytes*=2;
  1928.         // We know the Mac IIci driver size to be 1896
  1929.         // when ROM version is 124 rev. 1, but who knows for later ROMs?
  1930.         //bytes=1896;
  1931.         handle=NewHandleSys(bytes);
  1932.         if(handle==NULL)return 0;    // Insufficient room on System heap.
  1933.         HLockHi(handle);
  1934.         BlockMove((Ptr)driver,*handle,bytes);
  1935.         (**auxDCEHandle).dCtlDriver=(Ptr)handle;
  1936.         (**auxDCEHandle).dCtlFlags |= dRAMBasedMask;        
  1937.     }
  1938.     
  1939.     // Patch RAM-based driver.
  1940.     handle=(Handle)(**auxDCEHandle).dCtlDriver;
  1941.     w=*(short **)handle;
  1942.     if(w[0x51e/2]!=0x818 || w[0x590/2]!=0x1810 || w[0x2c/2]!=badVersion){
  1943.         printf("PatchMacIIciVideoDriver error.\n");
  1944.         return 0;
  1945.     }
  1946.     w[0x51e/2]=0x4858;
  1947.     w[0x590/2]=0x1a12;
  1948.     w[0x2c/2]+=100;                                    // Change version number.
  1949.     if(w[0x51e/2]!=0x4858 || w[0x590/2]!=0x1a12){
  1950.         printf("PatchMacIIciVideoDriver error.\n");
  1951.         return 0;
  1952.     }
  1953.     HNoPurge(handle);
  1954.     return 1;
  1955. }
  1956.  
  1957. /*
  1958. Received:    10/24
  1959. From:        Fernando Urbina, nano@apple.com
  1960. To:          Denis Pelli, denis@cns.nyu.edu
  1961.  
  1962. The following is the information on the private call for the 7500 and the 
  1963. 8500 to disable waiting for VBL's and to change the time that we wait for 
  1964. the CLUT to settle after writing each entry in the CLUT.  As shipped, the 
  1965. driver waits for 800 nanoseconds after writing each RGB triplet, to allow 
  1966. the hardware to increment the CLUT address.
  1967.  
  1968. cscSetTimeDelays is used to set some flags and time delays used to write the
  1969. CLUT. Use PBControl to issue the call, with the csParam[0] containing a pointer
  1970. to a VDTimeDelay structure. Set the "validMask" field with the bits
  1971. indicating which parameters you want to set.
  1972.  
  1973. cscGetTimeDelays is used to examine the current values of the flags and time
  1974. delays. Use PBStatus instead of PBControl. Set the "validMask" field
  1975. with the bits indicating which parameters you want to get.
  1976.  
  1977. The following is the definition of the fields of the VDTimeDelay struct:
  1978.  
  1979. flags, bit 0:  DontWaitForVBL flag:  When false, it means that the driver should
  1980. wait for a VBL before writing the CLUT.  When true, the driver should NOT wait
  1981. for a VBL. (6/3/97: I just discovered that this bit only affects cscSetEntries;
  1982. cscGetEntries is unaffected, and never waits for VBL. dgp)
  1983.  
  1984. flags, bit 1:  SetCLUTAddrRegTiming flag:  When false, use the default timing.
  1985. When true, use paramOne and paramTwo to specify the delay in nanoseconds.
  1986.  
  1987. validMask: Specifies which bits in flags are valid.  Bits are valid when
  1988. corresponding bit position is 1.
  1989.  
  1990. paramOne,paramTwo: When the SetCLUTAddrRegTiming bit of the validMask is set,
  1991. then these fields specify the delay, in nanoseconds: nanoseconds.hi in paramOne
  1992. and nanoseconds.lo in paramTwo.  See Designing PCI Cards & Drivers for the
  1993. Nanoseconds data structure.
  1994.  
  1995. */
  1996. enum{cscSetTimeDelays = 141};    // Used to set the different time delays
  1997. enum{cscGetTimeDelays = 141};
  1998. enum{gDontWaitForVBLMask=1,gSetCLUTTimingMask=2};
  1999. #if PRAGMA_ALIGN_SUPPORTED || __MWERKS__
  2000.     #pragma options align=mac68k
  2001. #endif
  2002. struct VDTimeDelays{
  2003.     UInt32 flags;
  2004.     UInt32 validMask;
  2005.     UInt32 paramOne;
  2006.     UInt32 paramTwo;
  2007. };
  2008. typedef struct VDTimeDelays VDTimeDelays;
  2009. #if PRAGMA_ALIGN_SUPPORTED || __MWERKS__
  2010.     #pragma options align=reset
  2011. #endif
  2012. OSErr GDSetTimeDelays(GDHandle device,VDTimeDelays *timeDelaysPtr);
  2013. OSErr GDGetTimeDelays(GDHandle device,VDTimeDelays *timeDelaysPtr);
  2014.  
  2015. OSErr GDSetDelay(GDHandle device,Boolean dontWaitForVBL,double nanoseconds)
  2016. {
  2017.     OSErr error;
  2018.     VDTimeDelays timeDelays={0,0,0,0};
  2019.     
  2020.     timeDelays.validMask=gDontWaitForVBLMask;
  2021.     if(dontWaitForVBL)timeDelays.flags=gDontWaitForVBLMask;
  2022.     else timeDelays.flags=0;
  2023.     if(IsFinite(nanoseconds)){
  2024.         timeDelays.flags|=gSetCLUTTimingMask;
  2025.         timeDelays.validMask|=gSetCLUTTimingMask;
  2026.         timeDelays.paramOne=nanoseconds/0x10000/0x10000;
  2027.         timeDelays.paramTwo=nanoseconds-(double)timeDelays.paramOne*0x10000*0x10000;
  2028.     }else timeDelays.paramOne=timeDelays.paramTwo=0; 
  2029.     error=GDSetTimeDelays(device,&timeDelays);
  2030.     return error;
  2031. }
  2032.  
  2033. OSErr GDGetDelay(GDHandle device,Boolean *dontWaitForVBLPtr,double *nanosecondsPtr)
  2034. {
  2035.     OSErr error;
  2036.     VDTimeDelays timeDelays={0,0,0,0};
  2037.     
  2038.     timeDelays.validMask=gDontWaitForVBLMask|gSetCLUTTimingMask;
  2039.     error=GDGetTimeDelays(device,&timeDelays);
  2040.     if(error)return error;
  2041.     if(dontWaitForVBLPtr!=NULL){
  2042.         if(timeDelays.flags&gDontWaitForVBLMask) *dontWaitForVBLPtr=1;
  2043.         else *dontWaitForVBLPtr=0;
  2044.     }
  2045.     if(nanosecondsPtr!=NULL)
  2046.         *nanosecondsPtr=(double)timeDelays.paramTwo+(double)timeDelays.paramOne*0x10000*0x10000;
  2047.     return error;
  2048. }
  2049.  
  2050. OSErr GDSetTimeDelays(GDHandle device,VDTimeDelays *timeDelaysPtr)
  2051. {
  2052.     OSErr error;
  2053.     
  2054.     if(device==NULL || (*device)->gdRefNum==0 || timeDelaysPtr==NULL) return controlErr;
  2055.     error=GDControl((*device)->gdRefNum,cscSetTimeDelays,(Ptr)timeDelaysPtr);
  2056.     return error;
  2057. }
  2058.  
  2059. OSErr GDGetTimeDelays(GDHandle device,VDTimeDelays *timeDelaysPtr)
  2060. {
  2061.     OSErr error;
  2062.     
  2063.     if(device==NULL || (*device)->gdRefNum==0 || timeDelaysPtr==NULL) return statusErr;
  2064.     error=GDStatus((*device)->gdRefNum,cscGetTimeDelays,(Ptr)timeDelaysPtr);
  2065.     return error;
  2066. }
  2067.  
  2068. Boolean IsPCIMac(void)
  2069. {
  2070.     static Boolean pci,firstTime=1;
  2071.  
  2072.         if(firstTime){
  2073.             long templong;
  2074.             int error;
  2075.             #define gestaltNameRegistryVersion 'nreg'    // support for old versions of Gestalt.h
  2076.             error=Gestalt(gestaltNameRegistryVersion,&templong);
  2077.             pci=(error==0);    // are there PCI slots?
  2078.             firstTime=0;
  2079.         }
  2080.         return pci;
  2081. }
  2082.  
  2083. void GDRestoreBlackAndWhite(GDHandle device)
  2084. // Restore the first & last clut entries to white and black.
  2085. {
  2086.     short clutSize;
  2087.     int error;
  2088.     ColorSpec white={255,0xffff,0xffff,0xffff},black={0,0,0,0};
  2089.  
  2090.     clutSize=GDClutSize(device);
  2091.     switch((**device).gdType){
  2092.     case clutType:
  2093.         error=GDSetEntries(device,0,0,&white);
  2094.         error=GDSetEntries(device,clutSize-1,0,&black);    // Fixed 3/4/96
  2095.         break;
  2096.     case directType:
  2097.         error=GDDirectSetEntries(device,0,0,&black);
  2098.         error=GDDirectSetEntries(device,clutSize-1,0,&white); // Fixed 3/4/96
  2099.         break;
  2100.     default:
  2101.         break;
  2102.     }
  2103. }
  2104.